@remotion/studio-server 4.0.495 → 4.0.497

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/better-opn/index.d.ts +6 -0
  2. package/dist/better-opn/index.js +46 -18
  3. package/dist/codemods/recast-mods.js +4 -15
  4. package/dist/codemods/strip-parenthesized-extra.d.ts +1 -0
  5. package/dist/codemods/strip-parenthesized-extra.js +16 -0
  6. package/dist/codemods/update-sequence-props/update-sequence-props.js +47 -0
  7. package/dist/helpers/css-shorthand-properties.d.ts +27 -0
  8. package/dist/helpers/css-shorthand-properties.js +43 -0
  9. package/dist/helpers/package-manager-spawn-options.d.ts +2 -0
  10. package/dist/helpers/package-manager-spawn-options.js +7 -0
  11. package/dist/helpers/parse-background-shorthand.d.ts +11 -0
  12. package/dist/helpers/parse-background-shorthand.js +41 -0
  13. package/dist/helpers/parse-border-shorthand.d.ts +6 -0
  14. package/dist/helpers/parse-border-shorthand.js +135 -0
  15. package/dist/helpers/resolve-composition-component.d.ts +3 -1
  16. package/dist/helpers/resolve-composition-component.js +40 -10
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.js +2 -0
  19. package/dist/open-browser-shortcut.js +26 -16
  20. package/dist/preview-server/routes/can-update-sequence-props.js +54 -3
  21. package/dist/preview-server/routes/download-remote-asset.js +1 -0
  22. package/dist/preview-server/routes/insert-element.js +7 -1
  23. package/dist/preview-server/routes/insert-jsx-element.js +9 -1
  24. package/dist/preview-server/routes/install-dependency.js +5 -1
  25. package/dist/preview-server/routes/save-sequence-props.js +90 -22
  26. package/package.json +6 -6
  27. package/dist/preview-server/routes/delete-effect-keyframe.d.ts +0 -3
  28. package/dist/preview-server/routes/delete-effect-keyframe.js +0 -89
  29. package/dist/preview-server/routes/delete-sequence-keyframe.d.ts +0 -3
  30. package/dist/preview-server/routes/delete-sequence-keyframe.js +0 -82
  31. package/dist/preview-server/routes/save-props-mutex.d.ts +0 -1
  32. package/dist/preview-server/routes/save-props-mutex.js +0 -11
@@ -44,6 +44,7 @@ const recast = __importStar(require("recast"));
44
44
  const no_react_1 = require("remotion/no-react");
45
45
  const format_file_content_1 = require("../codemods/format-file-content");
46
46
  const parse_ast_1 = require("../codemods/parse-ast");
47
+ const strip_parenthesized_extra_1 = require("../codemods/strip-parenthesized-extra");
47
48
  const update_nested_prop_1 = require("../codemods/update-nested-prop");
48
49
  const imports_1 = require("./imports");
49
50
  const svg_to_jsx_1 = require("./svg-to-jsx");
@@ -577,16 +578,18 @@ const createSolidElement = ({ localName, width, height, position, }) => {
577
578
  createPositionAbsoluteStyleAttribute(position),
578
579
  ], true), null, []);
579
580
  };
580
- const createComponentElement = ({ addPositionStyle, localName, props, position, }) => {
581
+ const createComponentElement = ({ addPositionStyle, from, localName, props, position, }) => {
581
582
  return recast.types.builders.jsxElement(recast.types.builders.jsxOpeningElement(recast.types.builders.jsxIdentifier(localName), [
582
583
  ...props.map(createComponentProp),
584
+ ...(from === null ? [] : [createNumberAttribute('from', from)]),
583
585
  ...(addPositionStyle
584
586
  ? [createPositionAbsoluteStyleAttribute(position)]
585
587
  : []),
586
588
  ], true), null, []);
587
589
  };
588
- const createSequenceWrappedElement = ({ child, dimensions, durationInFrames, name, position, sequenceLocalName, }) => {
590
+ const createSequenceWrappedElement = ({ child, dimensions, durationInFrames, from, name, position, sequenceLocalName, }) => {
589
591
  return recast.types.builders.jsxElement(recast.types.builders.jsxOpeningElement(recast.types.builders.jsxIdentifier(sequenceLocalName), [
592
+ ...(from === null ? [] : [createNumberAttribute('from', from)]),
590
593
  ...(name === null ? [] : [createStringAttribute('name', name)]),
591
594
  ...(dimensions !== null
592
595
  ? [
@@ -600,22 +603,29 @@ const createSequenceWrappedElement = ({ child, dimensions, durationInFrames, nam
600
603
  createPositionAbsoluteStyleAttribute(position),
601
604
  ], false), recast.types.builders.jsxClosingElement(recast.types.builders.jsxIdentifier(sequenceLocalName)), [child]);
602
605
  };
603
- const createAssetElement = ({ addPositionStyle, localName, staticFileLocalName, src, dimensions, position, }) => {
606
+ const createAssetElement = ({ addPositionStyle, durationInFrames, from, localName, staticFileLocalName, src, dimensions, position, }) => {
604
607
  return recast.types.builders.jsxElement(recast.types.builders.jsxOpeningElement(recast.types.builders.jsxIdentifier(localName), [
605
608
  staticFileLocalName === null
606
609
  ? createStringSrcAttribute(src)
607
610
  : createStaticFileSrcAttribute({ staticFileLocalName, src }),
611
+ ...(durationInFrames === null
612
+ ? []
613
+ : [createNumberAttribute('durationInFrames', durationInFrames)]),
614
+ ...(from === null ? [] : [createNumberAttribute('from', from)]),
608
615
  ...(addPositionStyle
609
616
  ? [createAssetStyleAttribute({ dimensions, position })]
610
617
  : []),
611
618
  ], true), null, []);
612
619
  };
613
- const createSvgElement = async ({ interactiveLocalName, markup, position, }) => {
620
+ const createSvgElement = async ({ from, interactiveLocalName, markup, position, }) => {
614
621
  var _a;
615
622
  var _b;
616
623
  const svgElement = await (0, svg_to_jsx_1.svgMarkupToJsx)(markup);
617
624
  const attributes = (_b = svgElement.openingElement.attributes) !== null && _b !== void 0 ? _b : [];
618
625
  svgElement.openingElement.attributes = attributes;
626
+ if (from !== null) {
627
+ attributes.push(createNumberAttribute('from', from));
628
+ }
619
629
  const styleAttribute = attributes.find((attribute) => attribute.type === 'JSXAttribute' &&
620
630
  attribute.name.type === 'JSXIdentifier' &&
621
631
  attribute.name.name === 'style');
@@ -1110,7 +1120,7 @@ const addElementToComponentRoot = ({ ast, exportName, element, }) => {
1110
1120
  if (rootNode.type === 'JSXElement' && rootNode.openingElement.selfClosing) {
1111
1121
  const fragment = recast.types.builders.jsxFragment(recast.types.builders.jsxOpeningFragment(), recast.types.builders.jsxClosingFragment(), [
1112
1122
  createSequenceWithChild({
1113
- child: rootNode,
1123
+ child: (0, strip_parenthesized_extra_1.stripParenthesizedExtra)(rootNode),
1114
1124
  sequenceLocalName: ensureSequenceImport(ast),
1115
1125
  }),
1116
1126
  element,
@@ -1316,7 +1326,7 @@ const resolveCompositionComponent = async ({ remotionRoot, compositionFile, comp
1316
1326
  };
1317
1327
  };
1318
1328
  exports.resolveCompositionComponent = resolveCompositionComponent;
1319
- const createInsertableJsxElement = ({ addPositionStyleToComponent, ast, destinationFileName, element, remotionRoot, }) => {
1329
+ const createInsertableJsxElement = ({ addPositionStyleToComponent, ast, destinationFileName, element, from, remotionRoot, }) => {
1320
1330
  if (element.type === 'solid') {
1321
1331
  const solidLocalName = ensureSolidImport(ast);
1322
1332
  return createSolidElement({
@@ -1335,6 +1345,7 @@ const createInsertableJsxElement = ({ addPositionStyleToComponent, ast, destinat
1335
1345
  });
1336
1346
  return createComponentElement({
1337
1347
  addPositionStyle: addPositionStyleToComponent,
1348
+ from,
1338
1349
  localName: componentLocalName,
1339
1350
  props: element.props,
1340
1351
  position: element.position,
@@ -1342,6 +1353,7 @@ const createInsertableJsxElement = ({ addPositionStyleToComponent, ast, destinat
1342
1353
  }
1343
1354
  if (element.type === 'svg') {
1344
1355
  return createSvgElement({
1356
+ from,
1345
1357
  interactiveLocalName: ensureInteractiveImport(ast),
1346
1358
  markup: element.markup,
1347
1359
  position: element.position,
@@ -1387,17 +1399,21 @@ const createInsertableJsxElement = ({ addPositionStyleToComponent, ast, destinat
1387
1399
  throw new Error('Unsupported asset type');
1388
1400
  }
1389
1401
  return createAssetElement({
1390
- addPositionStyle: element.assetType !== 'audio',
1402
+ addPositionStyle: addPositionStyleToComponent && element.assetType !== 'audio',
1403
+ durationInFrames: element.assetType === 'image' ? null : element.durationInFrames,
1404
+ from,
1391
1405
  localName,
1392
1406
  staticFileLocalName,
1393
1407
  src: element.src,
1394
- dimensions: element.dimensions,
1408
+ dimensions: element.assetType === 'image' && from !== null
1409
+ ? null
1410
+ : element.dimensions,
1395
1411
  position: element.position,
1396
1412
  });
1397
1413
  }
1398
1414
  throw new Error('Unsupported element type');
1399
1415
  };
1400
- const insertJsxElementIntoComposition = async ({ remotionRoot, compositionFile, compositionId, element, prettierConfigOverride, wrapInSequence = null, }) => {
1416
+ const insertJsxElementIntoComposition = async ({ remotionRoot, compositionFile, compositionId, element, from, prettierConfigOverride, wrapInSequence = null, }) => {
1401
1417
  var _a;
1402
1418
  const location = await (0, exports.resolveCompositionComponentWithFile)({
1403
1419
  remotionRoot,
@@ -1422,13 +1438,26 @@ const insertJsxElementIntoComposition = async ({ remotionRoot, compositionFile,
1422
1438
  durationInFrames: element.durationInFrames,
1423
1439
  name: element.compositionId,
1424
1440
  position: element.position,
1441
+ from,
1425
1442
  }
1426
- : wrapInSequence;
1443
+ : from === null ||
1444
+ element.type === 'asset' ||
1445
+ element.type === 'svg' ||
1446
+ element.type === 'component'
1447
+ ? wrapInSequence
1448
+ : {
1449
+ dimensions: null,
1450
+ durationInFrames: null,
1451
+ name: null,
1452
+ position: element.position,
1453
+ from,
1454
+ };
1427
1455
  const elementToInsert = await createInsertableJsxElement({
1428
1456
  addPositionStyleToComponent: sequenceWrapper === null,
1429
1457
  ast,
1430
1458
  destinationFileName: location.fileName,
1431
1459
  element,
1460
+ from,
1432
1461
  remotionRoot,
1433
1462
  });
1434
1463
  const finalElementToInsert = sequenceWrapper
@@ -1436,6 +1465,7 @@ const insertJsxElementIntoComposition = async ({ remotionRoot, compositionFile,
1436
1465
  child: elementToInsert,
1437
1466
  dimensions: sequenceWrapper.dimensions,
1438
1467
  durationInFrames: (_a = sequenceWrapper.durationInFrames) !== null && _a !== void 0 ? _a : null,
1468
+ from: sequenceWrapper.from,
1439
1469
  name: sequenceWrapper.name,
1440
1470
  position: sequenceWrapper.position,
1441
1471
  sequenceLocalName: ensureSequenceImport(ast),
package/dist/index.d.ts CHANGED
@@ -115,6 +115,7 @@ export declare const StudioServerInternals: {
115
115
  version: string;
116
116
  additionalArgs: string[];
117
117
  }) => string[];
118
+ getPackageManagerSpawnOptions: () => Pick<import("child_process").SpawnOptionsWithoutStdio, "shell">;
118
119
  addCompletedClientRender: ({ render, remotionRoot, }: {
119
120
  render: import("@remotion/studio-shared").CompletedClientRender;
120
121
  remotionRoot: string;
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ const file_watcher_1 = require("./file-watcher");
13
13
  const get_latest_remotion_version_1 = require("./get-latest-remotion-version");
14
14
  const get_installed_dependencies_1 = require("./helpers/get-installed-dependencies");
15
15
  const install_command_1 = require("./helpers/install-command");
16
+ const package_manager_spawn_options_1 = require("./helpers/package-manager-spawn-options");
16
17
  const max_timeline_tracks_1 = require("./max-timeline-tracks");
17
18
  const get_package_manager_1 = require("./preview-server/get-package-manager");
18
19
  const live_events_1 = require("./preview-server/live-events");
@@ -41,6 +42,7 @@ exports.StudioServerInternals = {
41
42
  getInstalledDependencies: get_installed_dependencies_1.getInstalledDependencies,
42
43
  getInstalledDependenciesWithVersions: get_installed_dependencies_1.getInstalledDependenciesWithVersions,
43
44
  getInstallCommand: install_command_1.getInstallCommand,
45
+ getPackageManagerSpawnOptions: package_manager_spawn_options_1.getPackageManagerSpawnOptions,
44
46
  addCompletedClientRender: client_render_queue_1.addCompletedClientRender,
45
47
  getCompletedClientRenders: client_render_queue_1.getCompletedClientRenders,
46
48
  removeCompletedClientRender: client_render_queue_1.removeCompletedClientRender,
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.registerOpenBrowserShortcut = void 0;
37
37
  const readline = __importStar(require("node:readline"));
38
38
  const renderer_1 = require("@remotion/renderer");
39
+ const better_opn_1 = require("./better-opn");
39
40
  const maybe_open_browser_1 = require("./maybe-open-browser");
40
41
  const live_events_1 = require("./preview-server/live-events");
41
42
  const registerOpenBrowserShortcut = ({ browserArgs, browserFlag, url, logLevel, }) => {
@@ -64,30 +65,39 @@ const registerOpenBrowserShortcut = ({ browserArgs, browserFlag, url, logLevel,
64
65
  process.stdin.pause();
65
66
  }
66
67
  };
67
- const openStudio = () => {
68
+ const openStudio = async () => {
68
69
  if (isOpeningBrowser) {
69
70
  return;
70
71
  }
71
72
  isOpeningBrowser = true;
72
73
  (0, live_events_1.clearPrintPortMessageTimeout)();
73
- (0, maybe_open_browser_1.maybeOpenBrowser)({
74
- browserArgs,
75
- browserFlag,
76
- shouldOpenBrowser: true,
77
- url,
78
- logLevel,
79
- })
80
- .then((result) => {
74
+ try {
75
+ const didFocus = await (0, better_opn_1.focusBrowserTabByOrigin)({
76
+ url,
77
+ browserArgs,
78
+ browserFlag,
79
+ });
80
+ if (didFocus) {
81
+ renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, renderer_1.RenderInternals.chalk.blue(`Opened ${url} in browser`));
82
+ return;
83
+ }
84
+ const result = await (0, maybe_open_browser_1.maybeOpenBrowser)({
85
+ browserArgs,
86
+ browserFlag,
87
+ shouldOpenBrowser: true,
88
+ url,
89
+ logLevel,
90
+ });
81
91
  if (result.didOpenBrowser) {
82
- renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, `Opened ${url} in browser`);
92
+ renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, renderer_1.RenderInternals.chalk.blue(`Opened ${url} in browser`));
83
93
  }
84
- })
85
- .catch((err) => {
94
+ }
95
+ catch (err) {
86
96
  renderer_1.RenderInternals.Log.error({ indent: false, logLevel }, 'Could not open browser:', err);
87
- })
88
- .finally(() => {
97
+ }
98
+ finally {
89
99
  isOpeningBrowser = false;
90
- });
100
+ }
91
101
  };
92
102
  function onKeypress(_str, key) {
93
103
  if ((key === null || key === void 0 ? void 0 : key.ctrl) && key.name === 'c') {
@@ -98,7 +108,7 @@ const registerOpenBrowserShortcut = ({ browserArgs, browserFlag, url, logLevel,
98
108
  if ((key === null || key === void 0 ? void 0 : key.meta) || (key === null || key === void 0 ? void 0 : key.ctrl) || (key === null || key === void 0 ? void 0 : key.name) !== 's') {
99
109
  return;
100
110
  }
101
- openStudio();
111
+ openStudio().catch(() => undefined);
102
112
  }
103
113
  readline.emitKeypressEvents(process.stdin);
104
114
  process.stdin.setRawMode(true);
@@ -40,6 +40,7 @@ const studio_shared_1 = require("@remotion/studio-shared");
40
40
  const recast = __importStar(require("recast"));
41
41
  const no_react_1 = require("remotion/no-react");
42
42
  const parse_ast_1 = require("../../codemods/parse-ast");
43
+ const css_shorthand_properties_1 = require("../../helpers/css-shorthand-properties");
43
44
  const get_ast_node_path_1 = require("../../helpers/get-ast-node-path");
44
45
  const import_agnostic_node_path_1 = require("../../helpers/import-agnostic-node-path");
45
46
  const parse_keyframe_easing_expression_1 = require("../../helpers/parse-keyframe-easing-expression");
@@ -669,6 +670,15 @@ const validateStyleValue = (childKey, value) => {
669
670
  }
670
671
  return true;
671
672
  };
673
+ const getObjectPropertyName = (property) => {
674
+ if (property.key.type === 'Identifier') {
675
+ return property.key.name;
676
+ }
677
+ if (property.key.type === 'StringLiteral') {
678
+ return property.key.value;
679
+ }
680
+ return null;
681
+ };
672
682
  const getNestedPropStatus = ({ jsxElement, ast, parentKey, childKey, videoConfigValues, allowSpecialValues, }) => {
673
683
  const attr = jsxElement.attributes.find((a) => a.type !== 'JSXSpreadAttribute' &&
674
684
  a.name.type !== 'JSXNamespacedName' &&
@@ -687,9 +697,50 @@ const getNestedPropStatus = ({ jsxElement, ast, parentKey, childKey, videoConfig
687
697
  return computedStatus();
688
698
  }
689
699
  const objExpr = expression;
690
- const prop = objExpr.properties.find((p) => p.type === 'ObjectProperty' &&
691
- ((p.key.type === 'Identifier' && p.key.name === childKey) ||
692
- (p.key.type === 'StringLiteral' && p.key.value === childKey)));
700
+ const cssShorthand = (0, css_shorthand_properties_1.getCssShorthandForLonghand)({
701
+ parentKey,
702
+ longhand: childKey,
703
+ });
704
+ if (cssShorthand &&
705
+ objExpr.properties.some((property) => {
706
+ if (property.type !== 'ObjectProperty') {
707
+ return false;
708
+ }
709
+ const propertyName = getObjectPropertyName(property);
710
+ return (propertyName !== null &&
711
+ cssShorthand.isUnsupportedProperty(propertyName));
712
+ })) {
713
+ return computedStatus();
714
+ }
715
+ let prop;
716
+ for (let index = objExpr.properties.length - 1; index >= 0; index--) {
717
+ const candidate = objExpr.properties[index];
718
+ if (candidate.type === 'SpreadElement') {
719
+ return computedStatus();
720
+ }
721
+ if (candidate.type === 'ObjectProperty' &&
722
+ (getObjectPropertyName(candidate) === childKey ||
723
+ getObjectPropertyName(candidate) === (cssShorthand === null || cssShorthand === void 0 ? void 0 : cssShorthand.shorthand))) {
724
+ prop = candidate;
725
+ break;
726
+ }
727
+ }
728
+ if (prop &&
729
+ cssShorthand &&
730
+ getObjectPropertyName(prop) === cssShorthand.shorthand) {
731
+ const shorthandValue = prop.value;
732
+ if (!(0, exports.isStaticValue)(shorthandValue, { allowSpecialValues: false })) {
733
+ return computedStatus();
734
+ }
735
+ const staticShorthandValue = (0, exports.extractStaticValue)(shorthandValue, {
736
+ allowSpecialValues: false,
737
+ });
738
+ if (typeof staticShorthandValue !== 'string') {
739
+ return computedStatus();
740
+ }
741
+ const parsed = cssShorthand.parse(staticShorthandValue);
742
+ return parsed ? staticStatus(parsed[childKey], null) : computedStatus();
743
+ }
693
744
  if (!prop) {
694
745
  // Property not set in the object, can be added
695
746
  return staticStatus(undefined, null);
@@ -263,6 +263,7 @@ const downloadRemoteAssetHandler = async ({ input, publicDir, request }) => {
263
263
  src: assetPath,
264
264
  srcType: 'static',
265
265
  dimensions: fileType.dimensions,
266
+ durationInFrames: null,
266
267
  position: null,
267
268
  };
268
269
  return {
@@ -69,10 +69,14 @@ const validateElement = (element) => {
69
69
  }
70
70
  validateDimensions(element.dimensions);
71
71
  };
72
- const insertElementHandler = ({ input: { compositionFile, compositionId, element, position }, remotionRoot, logLevel, }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
72
+ const insertElementHandler = ({ input: { compositionFile, compositionId, element, from, position }, remotionRoot, logLevel, }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
73
73
  try {
74
74
  validateElement(element);
75
75
  validatePosition(position);
76
+ if (from !== null &&
77
+ (!Number.isInteger(from) || !Number.isFinite(from) || from < 0)) {
78
+ throw new Error('from must be a non-negative integer');
79
+ }
76
80
  const componentName = (0, studio_shared_1.getElementComponentNameFromSourceCode)(element.sourceCode);
77
81
  if (componentName === null) {
78
82
  throw new Error('Element source must export exactly one named component');
@@ -118,9 +122,11 @@ const insertElementHandler = ({ input: { compositionFile, compositionId, element
118
122
  props: [],
119
123
  position: null,
120
124
  },
125
+ from: null,
121
126
  prettierConfigOverride: null,
122
127
  wrapInSequence: {
123
128
  dimensions: element.dimensions,
129
+ from,
124
130
  name: element.displayName,
125
131
  position,
126
132
  },
@@ -72,6 +72,9 @@ const validateElement = (element, remotionRoot) => {
72
72
  validateDimension('width', element.dimensions.width);
73
73
  validateDimension('height', element.dimensions.height);
74
74
  }
75
+ if (element.durationInFrames !== null) {
76
+ validateDimension('durationInFrames', element.durationInFrames);
77
+ }
75
78
  return;
76
79
  }
77
80
  if (element.type === 'component') {
@@ -145,9 +148,13 @@ const getElementLabel = (element) => {
145
148
  }
146
149
  throw new Error('Unsupported element type');
147
150
  };
148
- const insertJsxElementHandler = ({ input: { compositionFile, compositionId, element }, remotionRoot, logLevel, }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
151
+ const insertJsxElementHandler = ({ input: { compositionFile, compositionId, element, from }, remotionRoot, logLevel, }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
149
152
  try {
150
153
  validateElement(element, remotionRoot);
154
+ if (from !== null &&
155
+ (!Number.isInteger(from) || !Number.isFinite(from) || from < 0)) {
156
+ throw new Error('from must be a non-negative integer');
157
+ }
151
158
  const elementLabel = getElementLabel(element);
152
159
  renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[insert-jsx-element] Received request for compositionFile="${compositionFile}" compositionId="${compositionId}" element="${element.type}"`);
153
160
  const { fileName, source, oldContents, output, formatted, logLine } = await (0, resolve_composition_component_1.insertJsxElementIntoComposition)({
@@ -155,6 +162,7 @@ const insertJsxElementHandler = ({ input: { compositionFile, compositionId, elem
155
162
  compositionFile,
156
163
  compositionId,
157
164
  element,
165
+ from,
158
166
  prettierConfigOverride: null,
159
167
  });
160
168
  (0, undo_stack_1.pushToUndoStack)({
@@ -6,6 +6,7 @@ const renderer_1 = require("@remotion/renderer");
6
6
  const studio_shared_1 = require("@remotion/studio-shared");
7
7
  const version_1 = require("remotion/version");
8
8
  const install_command_1 = require("../../helpers/install-command");
9
+ const package_manager_spawn_options_1 = require("../../helpers/package-manager-spawn-options");
9
10
  const get_package_manager_1 = require("../get-package-manager");
10
11
  const getExtraPackageVersion = (packageName) => {
11
12
  const pkg = studio_shared_1.extraPackages.find((p) => p.name === packageName);
@@ -52,7 +53,10 @@ const handleInstallPackage = async ({ logLevel, remotionRoot, input: { packageNa
52
53
  const time = Date.now();
53
54
  try {
54
55
  await new Promise((resolve, reject) => {
55
- const cmd = (0, node_child_process_1.spawn)(manager.manager, command, {});
56
+ const cmd = (0, node_child_process_1.spawn)(manager.manager, command, (0, package_manager_spawn_options_1.getPackageManagerSpawnOptions)());
57
+ cmd.on('error', (err) => {
58
+ reject(err);
59
+ });
56
60
  cmd.stdout.on('data', (d) => {
57
61
  const splitted = d.toString().trim().split('\n');
58
62
  splitted.forEach((line) => {
@@ -59,14 +59,20 @@ const shouldSuppressHmrForSequencePropEdits = (edits) => {
59
59
  (edit.sourceEdit === null || edit.sourceEdit === undefined));
60
60
  };
61
61
  exports.shouldSuppressHmrForSequencePropEdits = shouldSuppressHmrForSequencePropEdits;
62
- const saveSequencePropsHandler = ({ input: { edits, movedKeyframes = { sequenceKeyframes: [], effectKeyframes: [] }, clientId, undoLabel, redoLabel, }, remotionRoot, logLevel, }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
63
- var _a, _b, _c;
64
- const totalKeyframeMoves = movedKeyframes.sequenceKeyframes.length +
65
- movedKeyframes.effectKeyframes.length;
66
- if (edits.length === 0 && totalKeyframeMoves === 0) {
62
+ const saveSequencePropsHandler = ({ input: { edits, addedKeyframes, movedKeyframes, clientId, undoLabel, redoLabel, }, remotionRoot, logLevel, }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
63
+ var _a, _b, _c, _d;
64
+ const keyframesToAdd = addedKeyframes === null ? [] : addedKeyframes;
65
+ const keyframesToMove = movedKeyframes === null
66
+ ? { sequenceKeyframes: [], effectKeyframes: [] }
67
+ : movedKeyframes;
68
+ const totalKeyframeMoves = keyframesToMove.sequenceKeyframes.length +
69
+ keyframesToMove.effectKeyframes.length;
70
+ if (edits.length === 0 &&
71
+ keyframesToAdd.length === 0 &&
72
+ totalKeyframeMoves === 0) {
67
73
  throw new Error('No sequence prop edits to save');
68
74
  }
69
- renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[save-sequence-props] Received request with ${edits.length} edit(s) and ${totalKeyframeMoves} moved keyframe(s)`);
75
+ renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[save-sequence-props] Received request with ${edits.length} edit(s), ${keyframesToAdd.length} added keyframe(s), and ${totalKeyframeMoves} moved keyframe(s)`);
70
76
  const editGroups = new Map();
71
77
  for (const [index, edit] of edits.entries()) {
72
78
  const parsedValue = parseSequencePropEditValue(edit.value);
@@ -79,7 +85,8 @@ const saveSequencePropsHandler = ({ input: { edits, movedKeyframes = { sequenceK
79
85
  const group = (_a = editGroups.get(absolutePath)) !== null && _a !== void 0 ? _a : {
80
86
  fileRelativeToRoot,
81
87
  edits: [],
82
- sequenceKeyframes: [],
88
+ addedKeyframes: [],
89
+ movedSequenceKeyframes: [],
83
90
  effectKeyframes: [],
84
91
  };
85
92
  group.edits.push({
@@ -98,7 +105,7 @@ const saveSequencePropsHandler = ({ input: { edits, movedKeyframes = { sequenceK
98
105
  });
99
106
  editGroups.set(absolutePath, group);
100
107
  }
101
- for (const [index, keyframe,] of movedKeyframes.sequenceKeyframes.entries()) {
108
+ for (const keyframe of keyframesToAdd) {
102
109
  const { absolutePath, fileRelativeToRoot } = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
103
110
  remotionRoot,
104
111
  fileName: keyframe.fileName,
@@ -107,13 +114,14 @@ const saveSequencePropsHandler = ({ input: { edits, movedKeyframes = { sequenceK
107
114
  const group = (_b = editGroups.get(absolutePath)) !== null && _b !== void 0 ? _b : {
108
115
  fileRelativeToRoot,
109
116
  edits: [],
110
- sequenceKeyframes: [],
117
+ addedKeyframes: [],
118
+ movedSequenceKeyframes: [],
111
119
  effectKeyframes: [],
112
120
  };
113
- group.sequenceKeyframes.push({ ...keyframe, index });
121
+ group.addedKeyframes.push(keyframe);
114
122
  editGroups.set(absolutePath, group);
115
123
  }
116
- for (const [index, keyframe] of movedKeyframes.effectKeyframes.entries()) {
124
+ for (const [index, keyframe,] of keyframesToMove.sequenceKeyframes.entries()) {
117
125
  const { absolutePath, fileRelativeToRoot } = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
118
126
  remotionRoot,
119
127
  fileName: keyframe.fileName,
@@ -122,7 +130,24 @@ const saveSequencePropsHandler = ({ input: { edits, movedKeyframes = { sequenceK
122
130
  const group = (_c = editGroups.get(absolutePath)) !== null && _c !== void 0 ? _c : {
123
131
  fileRelativeToRoot,
124
132
  edits: [],
125
- sequenceKeyframes: [],
133
+ addedKeyframes: [],
134
+ movedSequenceKeyframes: [],
135
+ effectKeyframes: [],
136
+ };
137
+ group.movedSequenceKeyframes.push({ ...keyframe, index });
138
+ editGroups.set(absolutePath, group);
139
+ }
140
+ for (const [index, keyframe] of keyframesToMove.effectKeyframes.entries()) {
141
+ const { absolutePath, fileRelativeToRoot } = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
142
+ remotionRoot,
143
+ fileName: keyframe.fileName,
144
+ action: 'modify',
145
+ });
146
+ const group = (_d = editGroups.get(absolutePath)) !== null && _d !== void 0 ? _d : {
147
+ fileRelativeToRoot,
148
+ edits: [],
149
+ addedKeyframes: [],
150
+ movedSequenceKeyframes: [],
126
151
  effectKeyframes: [],
127
152
  };
128
153
  group.effectKeyframes.push({ ...keyframe, index });
@@ -158,7 +183,40 @@ const saveSequencePropsHandler = ({ input: { edits, movedKeyframes = { sequenceK
158
183
  });
159
184
  }
160
185
  }
161
- for (const keyframeGroup of groupBy(group.sequenceKeyframes, (keyframe) => JSON.stringify(keyframe.nodePath.nodePath))) {
186
+ for (const keyframeGroup of groupBy(group.addedKeyframes, (keyframe) => JSON.stringify(keyframe.nodePath.nodePath))) {
187
+ const [firstSequenceKeyframe] = keyframeGroup;
188
+ if (!firstSequenceKeyframe) {
189
+ continue;
190
+ }
191
+ const updates = keyframeGroup.map((keyframe) => ({
192
+ key: keyframe.key,
193
+ operation: {
194
+ type: 'add',
195
+ frame: keyframe.frame,
196
+ value: JSON.parse(keyframe.value),
197
+ },
198
+ }));
199
+ const result = await (0, update_keyframes_1.updateSequenceKeyframes)({
200
+ input: output,
201
+ nodePath: firstSequenceKeyframe.nodePath.nodePath,
202
+ schema: firstSequenceKeyframe.schema,
203
+ videoConfigValues: firstSequenceKeyframe.nodePath.videoConfigValues,
204
+ updates,
205
+ });
206
+ output = result.output;
207
+ firstLogLine = Math.min(firstLogLine, result.logLine);
208
+ for (const [updateIndex, update] of updates.entries()) {
209
+ sequenceKeyframeLogs.push({
210
+ fileRelativeToRoot: group.fileRelativeToRoot,
211
+ line: result.logLine,
212
+ key: update.key,
213
+ oldValueString: result.oldValueStrings[updateIndex],
214
+ newValueString: result.newValueStrings[updateIndex],
215
+ formatted: result.formatted,
216
+ });
217
+ }
218
+ }
219
+ for (const keyframeGroup of groupBy(group.movedSequenceKeyframes, (keyframe) => JSON.stringify(keyframe.nodePath.nodePath))) {
162
220
  const [firstSequenceKeyframe] = keyframeGroup;
163
221
  if (!firstSequenceKeyframe) {
164
222
  continue;
@@ -318,10 +376,16 @@ const saveSequencePropsHandler = ({ input: { edits, movedKeyframes = { sequenceK
318
376
  });
319
377
  }
320
378
  (0, undo_stack_1.printUndoHint)(logLevel);
321
- const results = edits.map((edit) => {
379
+ const statusTargets = [
380
+ ...new Map([...edits, ...keyframesToAdd].map((target) => [
381
+ JSON.stringify(target.nodePath),
382
+ target,
383
+ ])).values(),
384
+ ];
385
+ const results = statusTargets.map((target) => {
322
386
  const { absolutePath } = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
323
387
  remotionRoot,
324
- fileName: edit.fileName,
388
+ fileName: target.fileName,
325
389
  action: 'modify',
326
390
  });
327
391
  const output = outputByPath.get(absolutePath);
@@ -330,22 +394,26 @@ const saveSequencePropsHandler = ({ input: { edits, movedKeyframes = { sequenceK
330
394
  }
331
395
  const newStatus = (0, can_update_sequence_props_1.computeSequencePropsStatusFromContent)({
332
396
  fileContents: output,
333
- keys: (0, studio_shared_1.getAllSchemaKeys)(edit.schema),
334
- assetKeys: (0, studio_shared_1.getAssetSchemaKeys)(edit.schema),
335
- nodePath: edit.nodePath.nodePath,
397
+ keys: (0, studio_shared_1.getAllSchemaKeys)(target.schema),
398
+ assetKeys: (0, studio_shared_1.getAssetSchemaKeys)(target.schema),
399
+ nodePath: target.nodePath.nodePath,
336
400
  componentIdentity: null,
337
401
  effects: [],
338
- videoConfigValues: edit.nodePath.videoConfigValues,
402
+ videoConfigValues: target.nodePath.videoConfigValues,
339
403
  });
340
404
  return {
341
- fileName: edit.fileName,
342
- nodePath: edit.nodePath,
405
+ fileName: target.fileName,
406
+ nodePath: target.nodePath,
343
407
  props: newStatus.props,
344
408
  };
345
409
  });
410
+ const firstResult = results[0];
411
+ if (!firstResult) {
412
+ throw new Error('Could not compute sequence prop edit status');
413
+ }
346
414
  return {
347
415
  canUpdate: true,
348
- props: results[0].props,
416
+ props: firstResult.props,
349
417
  results,
350
418
  };
351
419
  });
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.495",
6
+ "version": "4.0.497",
7
7
  "description": "Run a Remotion Studio with a server backend",
8
8
  "main": "dist",
9
9
  "scripts": {
@@ -30,11 +30,11 @@
30
30
  "kiwi-schema": "0.5.0",
31
31
  "semver": "7.5.3",
32
32
  "prettier": "3.8.1",
33
- "remotion": "4.0.495",
33
+ "remotion": "4.0.497",
34
34
  "recast": "0.23.11",
35
- "@remotion/bundler": "4.0.495",
36
- "@remotion/renderer": "4.0.495",
37
- "@remotion/studio-shared": "4.0.495",
35
+ "@remotion/bundler": "4.0.497",
36
+ "@remotion/renderer": "4.0.497",
37
+ "@remotion/studio-shared": "4.0.497",
38
38
  "memfs": "3.4.3",
39
39
  "open": "8.4.2"
40
40
  },
@@ -42,7 +42,7 @@
42
42
  "ast-types": "0.16.1",
43
43
  "react": "19.2.3",
44
44
  "@types/semver": "7.5.3",
45
- "@remotion/eslint-config-internal": "4.0.495",
45
+ "@remotion/eslint-config-internal": "4.0.497",
46
46
  "eslint": "9.19.0",
47
47
  "@types/node": "20.12.14",
48
48
  "@typescript/native-preview": "7.0.0-dev.20260217.1"
@@ -1,3 +0,0 @@
1
- import type { DeleteEffectKeyframeRequest, DeleteEffectKeyframeResponse } from '@remotion/studio-shared';
2
- import type { ApiHandler } from '../api-types';
3
- export declare const deleteEffectKeyframeHandler: ApiHandler<DeleteEffectKeyframeRequest, DeleteEffectKeyframeResponse>;