@remotion/studio-server 4.0.522 → 4.0.524

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 (31) hide show
  1. package/dist/canvas-capture/generate-canvas-capture-composition.d.ts +1 -1
  2. package/dist/canvas-capture/generate-canvas-capture-composition.js +77 -12
  3. package/dist/codemods/apply-codemod-to-file.js +8 -1
  4. package/dist/preview-server/api-routes.d.ts +1 -1
  5. package/dist/preview-server/api-routes.js +0 -2
  6. package/dist/preview-server/routes/apply-codemod.js +2 -3
  7. package/dist/preview-server/routes/delete-jsx-node.js +1 -1
  8. package/dist/preview-server/routes/duplicate-jsx-node.js +1 -1
  9. package/dist/preview-server/routes/element-install-plan.d.ts +1 -2
  10. package/dist/preview-server/routes/element-install-plan.js +7 -9
  11. package/dist/preview-server/routes/insert-element.js +37 -20
  12. package/dist/preview-server/routes/insert-jsx-element.js +7 -1
  13. package/dist/preview-server/routes/install-dependency.d.ts +16 -2
  14. package/dist/preview-server/routes/install-dependency.js +16 -2
  15. package/dist/preview-server/routes/reorder-sequence.js +1 -1
  16. package/dist/preview-server/routes/split-jsx-sequence.js +1 -1
  17. package/dist/preview-server/routes/split-video-from-audio.js +1 -1
  18. package/dist/preview-server/routes/update-config.js +6 -0
  19. package/dist/preview-server/sequence-node-path-mutation.d.ts +5 -1
  20. package/dist/preview-server/sequence-node-path-mutation.js +2 -1
  21. package/dist/preview-server/start-server.js +17 -0
  22. package/dist/preview-server/studio-protocol/handle-discovery.js +1 -1
  23. package/dist/preview-server/studio-protocol/handle-element-library.js +1 -1
  24. package/dist/preview-server/studio-protocol/handle-install.js +4 -3
  25. package/dist/preview-server/studio-protocol/handle-license-key.js +1 -1
  26. package/dist/preview-server/studio-protocol/origin-policy.d.ts +1 -1
  27. package/dist/preview-server/studio-protocol/origin-policy.js +13 -6
  28. package/dist/preview-server/undo-stack.js +2 -2
  29. package/dist/routes.d.ts +2 -1
  30. package/dist/routes.js +19 -1
  31. package/package.json +8 -8
@@ -11,4 +11,4 @@ export declare const generateCanvasCaptureComposition: ({ componentName, composi
11
11
  readonly videoHeight: number;
12
12
  readonly videoWidth: number;
13
13
  readonly width: number;
14
- }) => Promise<string>;
14
+ }) => string;
@@ -1,7 +1,40 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.generateCanvasCaptureComposition = void 0;
4
- const duplicate_composition_1 = require("../codemods/duplicate-composition");
37
+ const recast = __importStar(require("recast"));
5
38
  const customCursorRegex = /^\s*url\(/;
6
39
  const collapseMouseMovementsByFrame = ({ data, keyframeFps, }) => {
7
40
  const movements = [];
@@ -30,7 +63,39 @@ const getCursorName = (cursor) => {
30
63
  }
31
64
  return ((_a = cursor.split(',').at(-1)) === null || _a === void 0 ? void 0 : _a.trim().toLowerCase()) || 'default';
32
65
  };
33
- const serialize = (value) => JSON.stringify(value);
66
+ const serialize = (value) => {
67
+ if (typeof value === 'string') {
68
+ return recast.prettyPrint(recast.types.builders.stringLiteral(value), {
69
+ quote: 'single',
70
+ }).code;
71
+ }
72
+ return JSON.stringify(value);
73
+ };
74
+ const serializeArray = (values, indentation) => {
75
+ const items = values.map(serialize);
76
+ const inline = `[${items.join(', ')}]`;
77
+ if (indentation * 2 + inline.length + 1 <= 80) {
78
+ return inline;
79
+ }
80
+ const lines = [];
81
+ const numeric = values.every((value) => typeof value === 'number');
82
+ for (const item of items) {
83
+ const previous = lines.at(-1);
84
+ if (numeric &&
85
+ previous &&
86
+ (indentation + 1) * 2 + previous.length + item.length + 2 <= 80) {
87
+ lines[lines.length - 1] = `${previous} ${item},`;
88
+ }
89
+ else {
90
+ lines.push(`${item},`);
91
+ }
92
+ }
93
+ return [
94
+ '[',
95
+ ...lines.map((line) => `${'\t'.repeat(indentation + 1)}${line}`),
96
+ `${'\t'.repeat(indentation)}]`,
97
+ ].join('\n');
98
+ };
34
99
  const generateCanvasCaptureComposition = ({ componentName, compositionId, data, durationInFrames, fps, height, keyframeFps, videoFileName, videoHeight, videoWidth, width, }) => {
35
100
  var _a;
36
101
  const movements = collapseMouseMovementsByFrame({ data, keyframeFps });
@@ -79,11 +144,11 @@ const generateCanvasCaptureComposition = ({ componentName, compositionId, data,
79
144
  }, []);
80
145
  const customCursor = (_a = movements.find((movement) => customCursorRegex.test(movement.cursor))) === null || _a === void 0 ? void 0 : _a.cursor;
81
146
  const cursorProp = cursorKeyframes.length === 1
82
- ? `cursor=${serialize(cursorKeyframes[0].value)}`
147
+ ? `cursor={${serialize(cursorKeyframes[0].value)}}`
83
148
  : `cursor={interpolate(
84
149
  frame,
85
- ${serialize(cursorKeyframes.map((keyframe) => keyframe.frame))},
86
- ${serialize(cursorKeyframes.map((keyframe) => keyframe.value))},
150
+ ${serializeArray(cursorKeyframes.map((keyframe) => keyframe.frame), 5)},
151
+ ${serializeArray(cursorKeyframes.map((keyframe) => keyframe.value), 5)},
87
152
  {
88
153
  easing: Easing.step1,
89
154
  extrapolateLeft: 'clamp',
@@ -94,8 +159,8 @@ const generateCanvasCaptureComposition = ({ componentName, compositionId, data,
94
159
  ? serialize(scaleKeyframes[0].value)
95
160
  : `interpolate(
96
161
  frame,
97
- ${serialize(scaleKeyframes.map((keyframe) => keyframe.frame))},
98
- ${serialize(scaleKeyframes.map((keyframe) => keyframe.value))},
162
+ ${serializeArray(scaleKeyframes.map((keyframe) => keyframe.frame), 6)},
163
+ ${serializeArray(scaleKeyframes.map((keyframe) => keyframe.value), 6)},
99
164
  {
100
165
  easing: Easing.step1,
101
166
  extrapolateLeft: 'clamp',
@@ -106,8 +171,8 @@ const generateCanvasCaptureComposition = ({ componentName, compositionId, data,
106
171
  ? serialize(positionKeyframes[0].value)
107
172
  : `interpolate(
108
173
  frame,
109
- ${serialize(positionKeyframes.map((keyframe) => keyframe.frame))},
110
- ${serialize(positionKeyframes.map((keyframe) => keyframe.value))},
174
+ ${serializeArray(positionKeyframes.map((keyframe) => keyframe.frame), 6)},
175
+ ${serializeArray(positionKeyframes.map((keyframe) => keyframe.value), 6)},
111
176
  {
112
177
  extrapolateLeft: 'clamp',
113
178
  extrapolateRight: 'clamp',
@@ -116,7 +181,7 @@ const generateCanvasCaptureComposition = ({ componentName, compositionId, data,
116
181
  const previewComponentName = componentName.endsWith('Composition')
117
182
  ? `${componentName.slice(0, -'Composition'.length)}Preview`
118
183
  : `${componentName}Preview`;
119
- return (0, duplicate_composition_1.formatOutput)(`import {MacOSCursor} from '@remotion/mac-cursors';
184
+ return `import {MacOSCursor} from '@remotion/mac-cursors';
120
185
  import {Video} from '@remotion/media';
121
186
  import {
122
187
  AbsoluteFill,
@@ -160,7 +225,7 @@ ${customCursor === undefined ? '' : `\t\t\t\tcustomCursor={${serialize(customCur
160
225
  export const ${componentName} = () => {
161
226
  return (
162
227
  <Composition
163
- id=${serialize(compositionId)}
228
+ id={${serialize(compositionId)}}
164
229
  component={${previewComponentName}}
165
230
  width={${width}}
166
231
  height={${height}}
@@ -169,6 +234,6 @@ export const ${componentName} = () => {
169
234
  />
170
235
  );
171
236
  };
172
- `);
237
+ `;
173
238
  };
174
239
  exports.generateCanvasCaptureComposition = generateCanvasCaptureComposition;
@@ -27,6 +27,13 @@ const applyCodemodToFile = async ({ filePath, codeMod, }) => {
27
27
  codeMod,
28
28
  input,
29
29
  });
30
- return (0, duplicate_composition_1.formatOutput)(newContents);
30
+ if (codeMod.type === 'new-composition' ||
31
+ codeMod.type === 'duplicate-composition' ||
32
+ codeMod.type === 'rename-composition' ||
33
+ codeMod.type === 'delete-composition') {
34
+ return newContents;
35
+ }
36
+ const formatted = await (0, duplicate_composition_1.formatOutput)(newContents);
37
+ return formatted;
31
38
  };
32
39
  exports.applyCodemodToFile = applyCodemodToFile;
@@ -1,6 +1,6 @@
1
1
  import type { ApiRoutes } from '@remotion/studio-shared';
2
2
  import type { ApiHandler } from './api-types';
3
- type StandardApiRoute = Exclude<keyof ApiRoutes, '/api/update-config'>;
3
+ type StandardApiRoute = Exclude<keyof ApiRoutes, '/api/install-package' | '/api/update-config'>;
4
4
  export declare const allApiRoutes: {
5
5
  [key in StandardApiRoute]: ApiHandler<ApiRoutes[key]['Request'], ApiRoutes[key]['Response']>;
6
6
  };
@@ -25,7 +25,6 @@ const duplicate_jsx_node_1 = require("./routes/duplicate-jsx-node");
25
25
  const find_in_file_1 = require("./routes/find-in-file");
26
26
  const insert_element_1 = require("./routes/insert-element");
27
27
  const insert_jsx_element_1 = require("./routes/insert-jsx-element");
28
- const install_dependency_1 = require("./routes/install-dependency");
29
28
  const log_studio_error_1 = require("./routes/log-studio-error");
30
29
  const move_keyframes_1 = require("./routes/move-keyframes");
31
30
  const open_in_editor_1 = require("./routes/open-in-editor");
@@ -120,7 +119,6 @@ exports.allApiRoutes = {
120
119
  '/api/restart-studio': restart_studio_1.handleRestartStudio,
121
120
  '/api/default-coding-agent-info': default_coding_agent_1.getDefaultCodingAgentInfoHandler,
122
121
  '/api/default-editor-info': default_editor_1.getDefaultEditorInfoHandler,
123
- '/api/install-package': install_dependency_1.handleInstallPackage,
124
122
  '/api/insert-jsx-element': insert_jsx_element_1.insertJsxElementHandler,
125
123
  '/api/insert-element': insert_element_1.insertElementHandler,
126
124
  '/api/prepare-element-install': prepare_element_install_1.prepareElementInstallHandler,
@@ -10,7 +10,6 @@ const renderer_1 = require("@remotion/renderer");
10
10
  const studio_codemods_1 = require("@remotion/studio-codemods");
11
11
  const generate_canvas_capture_composition_1 = require("../../canvas-capture/generate-canvas-capture-composition");
12
12
  const apply_codemod_to_file_1 = require("../../codemods/apply-codemod-to-file");
13
- const duplicate_composition_1 = require("../../codemods/duplicate-composition");
14
13
  const file_watcher_1 = require("../../file-watcher");
15
14
  const format_log_file_location_1 = require("../format-log-file-location");
16
15
  const project_info_1 = require("../project-info");
@@ -33,12 +32,12 @@ const formatNewCompositionFile = (codemod) => {
33
32
  width: codemod.newWidth,
34
33
  });
35
34
  }
36
- return (0, duplicate_composition_1.formatOutput)(`import React from 'react';
35
+ return `import React from 'react';
37
36
 
38
37
  export const ${codemod.componentName}: React.FC = () => {
39
38
  return null;
40
39
  };
41
- `);
40
+ `;
42
41
  };
43
42
  const getFolderPath = (parentName, folderName) => {
44
43
  return parentName ? `${parentName}/${folderName}` : folderName;
@@ -65,7 +65,7 @@ const deleteJsxNodeHandler = ({ input: { nodes }, remotionRoot, logLevel }) => {
65
65
  const nodePathMutation = (0, sequence_node_path_mutation_1.broadcastSequenceNodePathMutation)(updates.map((update) => ({
66
66
  absolutePath: update.absolutePath,
67
67
  remappings: update.nodePathRemappings,
68
- })));
68
+ })), null);
69
69
  for (const update of updates) {
70
70
  const deletedNodeDescription = getDeletedNodeDescription(update.nodeLabels);
71
71
  (0, undo_stack_1.pushToUndoStack)({
@@ -50,7 +50,7 @@ const duplicateJsxNodeHandler = ({ input: { nodes }, remotionRoot, logLevel }) =
50
50
  const nodePathMutation = (0, sequence_node_path_mutation_1.broadcastSequenceNodePathMutation)(updates.map((update) => ({
51
51
  absolutePath: update.absolutePath,
52
52
  remappings: update.nodePathRemappings,
53
- })));
53
+ })), null);
54
54
  const duplicatedNodeDescription = nodes.length === 1
55
55
  ? updates[0].nodeLabels[0]
56
56
  : `${nodes.length} JSX nodes`;
@@ -1,5 +1,4 @@
1
1
  import type { ElementInstallExpectedFileState, PrepareElementInstallRequest } from '@remotion/studio-shared';
2
- export declare const normalizeElementSourceForComparison: (source: string) => string;
3
2
  export declare const getElementSourceHash: (source: string) => string;
4
3
  export declare const validateElementInstallPosition: (position: import("@remotion/studio-shared").InsertableCompositionElementPosition | null) => void;
5
4
  export declare const validateElementForInstallation: (element: import("@remotion/studio-shared").InstallableElement) => void;
@@ -7,7 +6,7 @@ export declare const makeRelativeElementImportPath: ({ fromFile, toFile, }: {
7
6
  fromFile: string;
8
7
  toFile: string;
9
8
  }) => string;
10
- export declare const getElementInstallPlan: ({ destination, element, entryPoint, remotionRoot, }: PrepareElementInstallRequest & {
9
+ export declare const getElementInstallPlan: ({ destination, element, installationName, entryPoint, remotionRoot, }: PrepareElementInstallRequest & {
11
10
  entryPoint: string;
12
11
  remotionRoot: string;
13
12
  }) => Promise<{
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getElementInstallPlan = exports.makeRelativeElementImportPath = exports.validateElementForInstallation = exports.validateElementInstallPosition = exports.getElementSourceHash = exports.normalizeElementSourceForComparison = void 0;
6
+ exports.getElementInstallPlan = exports.makeRelativeElementImportPath = exports.validateElementForInstallation = exports.validateElementInstallPosition = exports.getElementSourceHash = void 0;
7
7
  const node_crypto_1 = require("node:crypto");
8
8
  const node_fs_1 = require("node:fs");
9
9
  const node_path_1 = __importDefault(require("node:path"));
@@ -11,10 +11,6 @@ const studio_protocol_1 = require("@remotion/studio-protocol");
11
11
  const resolve_composition_component_1 = require("../../helpers/resolve-composition-component");
12
12
  const project_info_1 = require("../project-info");
13
13
  const safe_element_install_path_1 = require("./safe-element-install-path");
14
- const normalizeElementSourceForComparison = (source) => {
15
- return source.replace(/\r\n/g, '\n').trim();
16
- };
17
- exports.normalizeElementSourceForComparison = normalizeElementSourceForComparison;
18
14
  const getElementSourceHash = (source) => {
19
15
  return (0, node_crypto_1.createHash)('sha256').update(source).digest('hex');
20
16
  };
@@ -75,7 +71,7 @@ const getExpectedFileState = (existingSource) => {
75
71
  sourceHash: (0, exports.getElementSourceHash)(existingSource),
76
72
  };
77
73
  };
78
- const getElementInstallPlan = async ({ destination, element, entryPoint, remotionRoot, }) => {
74
+ const getElementInstallPlan = async ({ destination, element, installationName, entryPoint, remotionRoot, }) => {
79
75
  var _a;
80
76
  (0, exports.validateElementForInstallation)(element);
81
77
  const componentName = studio_protocol_1.StudioProtocolInternals.getElementComponentNameFromSourceCode(element.sourceCode);
@@ -92,9 +88,11 @@ const getElementInstallPlan = async ({ destination, element, entryPoint, remotio
92
88
  if (location !== null && !location.canAddSequence) {
93
89
  throw new Error('Cannot insert Element into this composition component');
94
90
  }
95
- const derivedElementFileName = studio_protocol_1.StudioProtocolInternals.makeElementFileNameFromSlug(element.slug);
96
- if (derivedElementFileName === null) {
97
- throw new Error('Element slug must produce a safe lowercase .tsx file name');
91
+ const derivedElementFileName = studio_protocol_1.StudioProtocolInternals.makeElementFileNameFromSlug(installationName !== null && installationName !== void 0 ? installationName : element.slug);
92
+ if (derivedElementFileName === null ||
93
+ (typeof installationName === 'string' &&
94
+ derivedElementFileName !== `${installationName}.element.tsx`)) {
95
+ throw new Error('Use a lowercase installation name with letters, numbers and hyphens, without a file extension.');
98
96
  }
99
97
  const destinationCompositionFile = destination.compositionFile;
100
98
  const destinationCompositionFileName = destinationCompositionFile === null
@@ -21,7 +21,9 @@ const hasExpectedFileState = ({ expected, actual, }) => {
21
21
  }
22
22
  return actual.sourceHash === expected.sourceHash;
23
23
  };
24
- const insertElementHandler = ({ input: { compositionFile, compositionId, element, expectedFileState, from, position, overwriteExisting, }, entryPoint, remotionRoot, logLevel, }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
24
+ const insertElementHandler = ({ input: { compositionFile, compositionId, element, installationName, expectedFileState, from, position, overwriteExisting, }, entryPoint, remotionRoot, logLevel, }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
25
+ var _a;
26
+ var _b;
25
27
  try {
26
28
  (0, element_install_plan_1.validateElementInstallPosition)(position);
27
29
  if (from !== null &&
@@ -32,8 +34,24 @@ const insertElementHandler = ({ input: { compositionFile, compositionId, element
32
34
  ? 'wrapped'
33
35
  : element.installationMode;
34
36
  const componentOwnsSequence = installationMode === 'component-owned-sequence';
37
+ if (componentOwnsSequence &&
38
+ element.initialProps !== null &&
39
+ ['from', 'durationInFrames', 'name'].some((prop) => {
40
+ var _a;
41
+ return Object.hasOwn((_a = element.initialProps) !== null && _a !== void 0 ? _a : {}, prop);
42
+ })) {
43
+ throw new Error('Component-owned Element initial props must not override from, durationInFrames, or name');
44
+ }
45
+ if (componentOwnsSequence &&
46
+ ((_a = element.initialProps) === null || _a === void 0 ? void 0 : _a.style) !== undefined &&
47
+ (element.initialProps.style === null ||
48
+ typeof element.initialProps.style !== 'object' ||
49
+ Array.isArray(element.initialProps.style))) {
50
+ throw new Error('Component-owned Element initial style must be an object');
51
+ }
35
52
  renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[insert-element] Received request for compositionFile="${compositionFile}" compositionId="${compositionId}" element="${element.slug}"`);
36
53
  const plan = await (0, element_install_plan_1.getElementInstallPlan)({
54
+ installationName,
37
55
  destination: {
38
56
  type: 'current-composition',
39
57
  compositionFile,
@@ -63,11 +81,8 @@ const insertElementHandler = ({ input: { compositionFile, compositionId, element
63
81
  throw new Error('Element source changed during installation');
64
82
  }
65
83
  const elementSourcesDiffer = plan.existingElementSource !== null &&
66
- (0, element_install_plan_1.normalizeElementSourceForComparison)(plan.existingElementSource) !==
67
- (0, element_install_plan_1.normalizeElementSourceForComparison)(element.sourceCode);
68
- if (elementSourcesDiffer &&
69
- !overwriteExisting &&
70
- plan.existingElementSource !== null) {
84
+ plan.existingElementSource !== element.sourceCode;
85
+ if (!overwriteExisting && plan.existingElementSource !== null) {
71
86
  return {
72
87
  success: false,
73
88
  type: 'file-conflict',
@@ -88,19 +103,20 @@ const insertElementHandler = ({ input: { compositionFile, compositionId, element
88
103
  componentName: plan.componentName,
89
104
  importName: plan.componentName,
90
105
  importPath: plan.importPath,
91
- props: componentOwnsSequence
92
- ? [
93
- ...(element.durationInFrames === null
94
- ? []
95
- : [
96
- {
97
- name: 'durationInFrames',
98
- value: element.durationInFrames,
99
- },
100
- ]),
101
- { name: 'name', value: element.displayName },
102
- ]
103
- : [],
106
+ props: [
107
+ ...Object.entries((_b = element.initialProps) !== null && _b !== void 0 ? _b : {}).map(([name, value]) => ({ name, value })),
108
+ ...(componentOwnsSequence && element.durationInFrames !== null
109
+ ? [
110
+ {
111
+ name: 'durationInFrames',
112
+ value: element.durationInFrames,
113
+ },
114
+ ]
115
+ : []),
116
+ ...(componentOwnsSequence
117
+ ? [{ name: 'name', value: element.displayName }]
118
+ : []),
119
+ ],
104
120
  position: componentOwnsSequence ? position : null,
105
121
  },
106
122
  from: componentOwnsSequence ? from : null,
@@ -116,6 +132,7 @@ const insertElementHandler = ({ input: { compositionFile, compositionId, element
116
132
  },
117
133
  });
118
134
  const finalPlan = await (0, element_install_plan_1.getElementInstallPlan)({
135
+ installationName,
119
136
  destination: {
120
137
  type: 'current-composition',
121
138
  compositionFile,
@@ -140,7 +157,7 @@ const insertElementHandler = ({ input: { compositionFile, compositionId, element
140
157
  absolutePath: inserted.fileName,
141
158
  remappings: inserted.nodePathRemappings,
142
159
  },
143
- ]);
160
+ ], null);
144
161
  (0, undo_stack_1.pushTransactionToUndoStack)({
145
162
  snapshots: [
146
163
  ...(shouldWriteElementFile
@@ -171,7 +171,13 @@ const insertJsxElementHandler = ({ input: { compositionFile, compositionId, elem
171
171
  absolutePath: fileName,
172
172
  remappings: nodePathRemappings,
173
173
  },
174
- ]);
174
+ ], insertedNodePath === null
175
+ ? null
176
+ : {
177
+ absolutePath: fileName,
178
+ compositionId,
179
+ nodePath: insertedNodePath,
180
+ });
175
181
  if (insertedNodePath === null) {
176
182
  renderer_1.RenderInternals.Log.warn({ indent: false, logLevel }, 'Could not determine the inserted JSX element node path. Skipping automatic selection.');
177
183
  }
@@ -1,4 +1,18 @@
1
1
  import { type InstallPackageRequest, type InstallPackageResponse, type PackageInstallSpec } from '@remotion/studio-shared';
2
- import type { ApiHandler } from '../api-types';
3
2
  export declare const getPackageInstallSpec: (dependency: PackageInstallSpec) => string;
4
- export declare const handleInstallPackage: ApiHandler<InstallPackageRequest, InstallPackageResponse>;
3
+ export declare const handleInstallPackage: ({ logLevel, remotionRoot, input: { dependencies }, invalidateBundle, }: {
4
+ input: InstallPackageRequest;
5
+ entryPoint: string;
6
+ remotionRoot: string;
7
+ request: import("http").IncomingMessage;
8
+ response: import("http").ServerResponse<import("http").IncomingMessage>;
9
+ logLevel: "error" | "info" | "trace" | "verbose" | "warn";
10
+ methods: import("../api-types").QueueMethods;
11
+ publicDir: string;
12
+ binariesDirectory: string | null;
13
+ configFile: string | null;
14
+ getDefaultCodingAgent: () => "claude-code" | "codex" | "copilot" | "cursor" | null;
15
+ getDefaultEditor: () => import("@remotion/renderer").DefaultEditor | null;
16
+ } & {
17
+ readonly invalidateBundle: () => Promise<void>;
18
+ }) => Promise<InstallPackageResponse>;
@@ -22,7 +22,7 @@ const getPackageInstallSpec = (dependency) => {
22
22
  return extraVersion === null ? name : `${name}@${extraVersion}`;
23
23
  };
24
24
  exports.getPackageInstallSpec = getPackageInstallSpec;
25
- const handleInstallPackage = async ({ logLevel, remotionRoot, input: { dependencies } }) => {
25
+ const handleInstallPackage = async ({ logLevel, remotionRoot, input: { dependencies }, invalidateBundle, }) => {
26
26
  for (const dependency of dependencies) {
27
27
  if (!(0, studio_shared_1.isValidPackageName)(dependency.name)) {
28
28
  return Promise.reject(new Error(`Package name ${JSON.stringify(dependency.name)} is invalid.`));
@@ -41,12 +41,24 @@ const handleInstallPackage = async ({ logLevel, remotionRoot, input: { dependenc
41
41
  if (manager === 'unknown') {
42
42
  throw new Error(`No lockfile was found in your project (one of ${get_package_manager_1.lockFilePaths.map((p) => p.path).join(', ')}). Install dependencies using your favorite manager!`);
43
43
  }
44
+ const additionalArgs = ['--ignore-scripts'];
45
+ if (manager.manager === 'yarn') {
46
+ const version = (0, node_child_process_1.execFileSync)('yarn', ['--version'], {
47
+ ...(0, package_manager_spawn_options_1.getPackageManagerSpawnOptions)(),
48
+ cwd: remotionRoot,
49
+ encoding: 'utf8',
50
+ timeout: 10000,
51
+ }).trim();
52
+ if (!/^1\./.test(version)) {
53
+ additionalArgs[0] = '--mode=skip-build';
54
+ }
55
+ }
44
56
  const packagesWithVersions = dependencies.map(exports.getPackageInstallSpec);
45
57
  const command = (0, install_command_1.getInstallCommand)({
46
58
  manager: manager.manager,
47
59
  packages: packagesWithVersions,
48
60
  version: '',
49
- additionalArgs: manager.manager === 'yarn' ? [] : ['--ignore-scripts'],
61
+ additionalArgs,
50
62
  });
51
63
  renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, renderer_1.RenderInternals.chalk.gray(`╭─ ${manager.manager} ${command.join(' ')}`));
52
64
  const time = Date.now();
@@ -54,6 +66,7 @@ const handleInstallPackage = async ({ logLevel, remotionRoot, input: { dependenc
54
66
  await new Promise((resolve, reject) => {
55
67
  const cmd = (0, node_child_process_1.spawn)(manager.manager, command, {
56
68
  ...(0, package_manager_spawn_options_1.getPackageManagerSpawnOptions)(),
69
+ cwd: remotionRoot,
57
70
  env: {
58
71
  ...process.env,
59
72
  YARN_ENABLE_SCRIPTS: 'false',
@@ -70,6 +83,7 @@ const handleInstallPackage = async ({ logLevel, remotionRoot, input: { dependenc
70
83
  ? resolve()
71
84
  : reject(new Error(`Command exited with code ${code} and signal ${signal}`)));
72
85
  });
86
+ await invalidateBundle();
73
87
  renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, renderer_1.RenderInternals.chalk.gray('╰─ '), `Done in ${Date.now() - time}ms`);
74
88
  return {};
75
89
  }
@@ -33,7 +33,7 @@ const reorderSequenceHandler = ({ input: { fileName, sourceNodePath, targetNodeP
33
33
  absolutePath,
34
34
  remappings: nodePathRemappings,
35
35
  },
36
- ]);
36
+ ], null);
37
37
  (0, undo_stack_1.pushToUndoStack)({
38
38
  filePath: absolutePath,
39
39
  oldContents: fileContents,
@@ -30,7 +30,7 @@ const splitJsxSequenceHandler = ({ input: { fileName, nodePath, sequenceKeys, sp
30
30
  absolutePath,
31
31
  remappings: nodePathRemappings,
32
32
  },
33
- ]);
33
+ ], null);
34
34
  (0, undo_stack_1.pushToUndoStack)({
35
35
  filePath: absolutePath,
36
36
  oldContents: fileContents,
@@ -29,7 +29,7 @@ const splitVideoFromAudioHandler = ({ input: { fileName, nodePath }, remotionRoo
29
29
  absolutePath,
30
30
  remappings: nodePathRemappings,
31
31
  },
32
- ]);
32
+ ], null);
33
33
  (0, undo_stack_1.pushToUndoStack)({
34
34
  filePath: absolutePath,
35
35
  oldContents: fileContents,
@@ -238,6 +238,12 @@ const updateConfigHandler = async ({ input, configFile, getStudioRuntimeConfig,
238
238
  if (update.type === 'delete') {
239
239
  continue;
240
240
  }
241
+ if (update.setter === 'setKeyboardShortcuts') {
242
+ const keyboardShortcutsError = (0, studio_shared_1.validateStudioKeyboardShortcuts)(update.value);
243
+ if (keyboardShortcutsError !== null) {
244
+ return { success: false, reason: keyboardShortcutsError };
245
+ }
246
+ }
241
247
  if (update.setter === 'setDefaultCodingAgent' &&
242
248
  (typeof update.value !== 'string' ||
243
249
  !renderer_1.defaultCodingAgentIds.includes(update.value))) {
@@ -2,4 +2,8 @@ import type { SequenceNodePathMutation, SequenceNodePathRemapping } from '@remot
2
2
  export declare const broadcastSequenceNodePathMutation: (files: {
3
3
  absolutePath: string;
4
4
  remappings: SequenceNodePathRemapping[];
5
- }[]) => SequenceNodePathMutation;
5
+ }[], timelineSelection: {
6
+ compositionId: string;
7
+ absolutePath: string;
8
+ nodePath: import("remotion").SequenceNodePath;
9
+ } | null) => SequenceNodePathMutation;
@@ -5,11 +5,12 @@ const node_crypto_1 = require("node:crypto");
5
5
  const live_events_1 = require("./live-events");
6
6
  const mutationSessionId = (0, node_crypto_1.randomUUID)();
7
7
  let mutationCounter = 0;
8
- const broadcastSequenceNodePathMutation = (files) => {
8
+ const broadcastSequenceNodePathMutation = (files, timelineSelection) => {
9
9
  var _a;
10
10
  mutationCounter++;
11
11
  const mutation = {
12
12
  mutationId: `${mutationSessionId}:${mutationCounter}`,
13
+ timelineSelection,
13
14
  files,
14
15
  };
15
16
  (_a = (0, live_events_1.getLiveEventsListener)()) === null || _a === void 0 ? void 0 : _a.sendEventToClient({
@@ -140,6 +140,22 @@ const startServer = async (options) => {
140
140
  }
141
141
  (0, watch_ignore_next_change_1.setWatchIgnoreNextChangePlugin)(watchIgnorePlugin);
142
142
  const wdmMiddleware = (0, dev_middleware_1.wdm)(compiler, options.logLevel);
143
+ const invalidateBundle = () => {
144
+ return new Promise((resolve, reject) => {
145
+ const watching = compiler === null || compiler === void 0 ? void 0 : compiler.watching;
146
+ if (!watching) {
147
+ reject(new Error('The Studio bundle is not being watched.'));
148
+ return;
149
+ }
150
+ watching.invalidate((error) => {
151
+ if (error) {
152
+ reject(error);
153
+ return;
154
+ }
155
+ resolve();
156
+ });
157
+ });
158
+ };
143
159
  const liveEventsServer = (0, live_events_1.makeLiveEventsRouter)(options.logLevel, () => {
144
160
  const undoStack = (0, undo_stack_1.getUndoStack)();
145
161
  const redoStack = (0, undo_stack_1.getRedoStack)();
@@ -196,6 +212,7 @@ const startServer = async (options) => {
196
212
  getDefaultCodingAgent: options.getDefaultCodingAgent,
197
213
  getDefaultEditor: options.getDefaultEditor,
198
214
  configFile: options.configFile,
215
+ invalidateBundle,
199
216
  });
200
217
  })
201
218
  .catch((err) => {
@@ -43,7 +43,7 @@ const getProject = ({ gitSource, remotionRoot, }) => (0, studio_shared_1.getProj
43
43
  });
44
44
  const handleStudioProtocolDiscovery = ({ gitSource, liveEventsServer, remotionRoot, request, response, }) => {
45
45
  (0, origin_policy_1.setStudioProtocolCorsHeaders)({ request, response });
46
- const requestOrigin = (0, origin_policy_1.getAllowedStudioProtocolOrigin)(request.headers.origin);
46
+ const requestOrigin = (0, origin_policy_1.getAllowedStudioProtocolOrigin)(request);
47
47
  if (requestOrigin === null) {
48
48
  (0, protocol_response_1.writeStudioProtocolError)({
49
49
  code: 'unsupported-origin',
@@ -11,7 +11,7 @@ const handleStudioProtocolElementLibrary = async ({ configFile, focusStudioTab,
11
11
  var _a;
12
12
  var _b;
13
13
  (0, origin_policy_1.setStudioProtocolCorsHeaders)({ request, response });
14
- const requestOrigin = (0, origin_policy_1.getAllowedStudioProtocolOrigin)(request.headers.origin);
14
+ const requestOrigin = (0, origin_policy_1.getAllowedStudioProtocolOrigin)(request);
15
15
  if (requestOrigin === null) {
16
16
  (0, protocol_response_1.writeStudioProtocolError)({
17
17
  code: 'unsupported-origin',
@@ -10,7 +10,7 @@ const protocol_response_1 = require("./protocol-response");
10
10
  // UTF-8 representation and the protocol envelope while keeping memory bounded.
11
11
  const MAX_STUDIO_PROTOCOL_INSTALL_BODY_SIZE = 1000000;
12
12
  const deliverElementInstall = ({ element, focusStudioTab, liveEventsServer, origin, target, }) => {
13
- var _a, _b;
13
+ var _a, _b, _c;
14
14
  if (target.compositionFile === null || target.compositionId === null) {
15
15
  return false;
16
16
  }
@@ -23,7 +23,8 @@ const deliverElementInstall = ({ element, focusStudioTab, liveEventsServer, orig
23
23
  element: {
24
24
  ...element,
25
25
  durationInFrames: (_a = element.durationInFrames) !== null && _a !== void 0 ? _a : null,
26
- installationMode: (_b = element.installationMode) !== null && _b !== void 0 ? _b : null,
26
+ initialProps: (_b = element.initialProps) !== null && _b !== void 0 ? _b : null,
27
+ installationMode: (_c = element.installationMode) !== null && _c !== void 0 ? _c : null,
27
28
  },
28
29
  from: null,
29
30
  position: null,
@@ -43,7 +44,7 @@ const deliverElementInstall = ({ element, focusStudioTab, liveEventsServer, orig
43
44
  };
44
45
  const handleStudioProtocolInstall = async ({ focusStudioTab, liveEventsServer, request, response, }) => {
45
46
  (0, origin_policy_1.setStudioProtocolCorsHeaders)({ request, response });
46
- const requestOrigin = (0, origin_policy_1.getAllowedStudioProtocolOrigin)(request.headers.origin);
47
+ const requestOrigin = (0, origin_policy_1.getAllowedStudioProtocolOrigin)(request);
47
48
  if (requestOrigin === null) {
48
49
  (0, protocol_response_1.writeStudioProtocolError)({
49
50
  code: 'unsupported-origin',
@@ -9,7 +9,7 @@ const protocol_response_1 = require("./protocol-response");
9
9
  const MAX_STUDIO_PROTOCOL_LICENSE_KEY_BODY_SIZE = 4096;
10
10
  const handleStudioProtocolLicenseKey = async ({ configFile, focusStudioTab, liveEventsServer, request, response, }) => {
11
11
  (0, origin_policy_1.setStudioProtocolCorsHeaders)({ request, response });
12
- const requestOrigin = (0, origin_policy_1.getAllowedStudioProtocolOrigin)(request.headers.origin);
12
+ const requestOrigin = (0, origin_policy_1.getAllowedStudioProtocolOrigin)(request);
13
13
  if (requestOrigin === null) {
14
14
  (0, protocol_response_1.writeStudioProtocolError)({
15
15
  code: 'unsupported-origin',
@@ -1,5 +1,5 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
- export declare const getAllowedStudioProtocolOrigin: (origin: string | undefined) => string | null;
2
+ export declare const getAllowedStudioProtocolOrigin: (request: IncomingMessage) => string | null;
3
3
  export declare const setStudioProtocolCorsHeaders: ({ request, response, }: {
4
4
  readonly request: IncomingMessage;
5
5
  readonly response: ServerResponse<IncomingMessage>;
@@ -3,11 +3,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.handleStudioProtocolOptions = exports.setStudioProtocolCorsHeaders = exports.getAllowedStudioProtocolOrigin = void 0;
4
4
  const isLoopbackHttp = (url) => url.protocol === 'http:' &&
5
5
  (url.hostname === 'localhost' || url.hostname === '127.0.0.1');
6
- const getAllowedStudioProtocolOrigin = (origin) => {
7
- if (!origin) {
8
- return null;
9
- }
6
+ const getAllowedStudioProtocolOrigin = (request) => {
10
7
  try {
8
+ const { origin, referer, host } = request.headers;
9
+ if (origin === undefined) {
10
+ // Browsers omit Origin on same-origin GET requests. Only trust a
11
+ // Referer matching this local HTTP Studio, never an arbitrary site.
12
+ const refererUrl = new URL(referer !== null && referer !== void 0 ? referer : '');
13
+ return isLoopbackHttp(refererUrl) &&
14
+ refererUrl.origin === `http://${host}`
15
+ ? refererUrl.origin
16
+ : null;
17
+ }
11
18
  const url = new URL(origin);
12
19
  if (url.protocol !== 'https:' && !isLoopbackHttp(url)) {
13
20
  return null;
@@ -20,7 +27,7 @@ const getAllowedStudioProtocolOrigin = (origin) => {
20
27
  };
21
28
  exports.getAllowedStudioProtocolOrigin = getAllowedStudioProtocolOrigin;
22
29
  const setStudioProtocolCorsHeaders = ({ request, response, }) => {
23
- const origin = (0, exports.getAllowedStudioProtocolOrigin)(request.headers.origin);
30
+ const origin = (0, exports.getAllowedStudioProtocolOrigin)(request);
24
31
  if (origin === null) {
25
32
  return;
26
33
  }
@@ -34,7 +41,7 @@ const setStudioProtocolCorsHeaders = ({ request, response, }) => {
34
41
  exports.setStudioProtocolCorsHeaders = setStudioProtocolCorsHeaders;
35
42
  const handleStudioProtocolOptions = ({ request, response, }) => {
36
43
  (0, exports.setStudioProtocolCorsHeaders)({ request, response });
37
- const origin = (0, exports.getAllowedStudioProtocolOrigin)(request.headers.origin);
44
+ const origin = (0, exports.getAllowedStudioProtocolOrigin)(request);
38
45
  response.writeHead(origin === null ? 403 : 204);
39
46
  response.end();
40
47
  return Promise.resolve();
@@ -264,7 +264,7 @@ function popUndo() {
264
264
  },
265
265
  ];
266
266
  });
267
- const nodePathMutation = files.length === 0 ? null : (0, sequence_node_path_mutation_1.broadcastSequenceNodePathMutation)(files);
267
+ const nodePathMutation = files.length === 0 ? null : (0, sequence_node_path_mutation_1.broadcastSequenceNodePathMutation)(files, null);
268
268
  for (const snapshot of entry.snapshots) {
269
269
  suppressUndoStackInvalidation(snapshot.filePath);
270
270
  if (entry.suppressHmrOnFileRestore) {
@@ -334,7 +334,7 @@ function popRedo() {
334
334
  },
335
335
  ];
336
336
  });
337
- const nodePathMutation = files.length === 0 ? null : (0, sequence_node_path_mutation_1.broadcastSequenceNodePathMutation)(files);
337
+ const nodePathMutation = files.length === 0 ? null : (0, sequence_node_path_mutation_1.broadcastSequenceNodePathMutation)(files, null);
338
338
  for (const snapshot of snapshotsWithNewContents) {
339
339
  suppressUndoStackInvalidation(snapshot.filePath);
340
340
  if (entry.suppressHmrOnFileRestore) {
package/dist/routes.d.ts CHANGED
@@ -3,7 +3,7 @@ import type { DefaultEditor } from '@remotion/renderer';
3
3
  import type { GitSource, RenderDefaults, RenderJob, StudioRuntimeConfig } from '@remotion/studio-shared';
4
4
  import type { QueueMethods } from './preview-server/api-types';
5
5
  import type { LiveEventsServer } from './preview-server/live-events';
6
- export declare const handleRoutes: ({ staticHash, staticHashPrefix, outputHash, outputHashPrefix, request, response, liveEventsServer, getCurrentInputProps, getEnvVariables, remotionRoot, entryPoint, publicDir, logLevel, getRenderQueue, getRenderDefaults, getNumberOfAudioTags, queueMethods: methods, gitSource, binariesDirectory, getAudioLatencyHint, getExperimentalKeepAudioContextAlive, getPreviewSampleRate, enableCrossSiteIsolation, getStudioRuntimeConfig, getDefaultCodingAgent, getDefaultEditor, configFile, }: {
6
+ export declare const handleRoutes: ({ staticHash, staticHashPrefix, outputHash, outputHashPrefix, request, response, liveEventsServer, getCurrentInputProps, getEnvVariables, remotionRoot, entryPoint, publicDir, logLevel, getRenderQueue, getRenderDefaults, getNumberOfAudioTags, queueMethods: methods, gitSource, binariesDirectory, getAudioLatencyHint, getExperimentalKeepAudioContextAlive, getPreviewSampleRate, enableCrossSiteIsolation, getStudioRuntimeConfig, getDefaultCodingAgent, getDefaultEditor, configFile, invalidateBundle, }: {
7
7
  staticHash: string;
8
8
  staticHashPrefix: string;
9
9
  outputHash: string;
@@ -31,4 +31,5 @@ export declare const handleRoutes: ({ staticHash, staticHashPrefix, outputHash,
31
31
  getDefaultCodingAgent: () => "claude-code" | "codex" | "copilot" | "cursor" | null;
32
32
  getDefaultEditor: () => DefaultEditor | null;
33
33
  configFile: string | null;
34
+ invalidateBundle: () => Promise<void>;
34
35
  }) => Promise<void>;
package/dist/routes.js CHANGED
@@ -52,6 +52,7 @@ const get_static_file_fallback_hint_1 = require("./preview-server/get-static-fil
52
52
  const handler_1 = require("./preview-server/handler");
53
53
  const public_folder_1 = require("./preview-server/public-folder");
54
54
  const app_icon_1 = require("./preview-server/routes/app-icon");
55
+ const install_dependency_1 = require("./preview-server/routes/install-dependency");
55
56
  const open_in_editor_1 = require("./preview-server/routes/open-in-editor");
56
57
  const update_config_1 = require("./preview-server/routes/update-config");
57
58
  const serve_static_1 = require("./preview-server/serve-static");
@@ -139,6 +140,7 @@ const handleFallback = async ({ remotionRoot, hash, response, request, getCurren
139
140
  (0, public_folder_1.fetchFolder)({ publicDir, staticHash: hash });
140
141
  const installedDependencies = (0, get_installed_installable_packages_1.getInstalledInstallablePackages)(remotionRoot);
141
142
  response.end(bundler_1.BundlerInternals.indexHtml({
143
+ importMap: null,
142
144
  staticHash: hash,
143
145
  publicPath: '/',
144
146
  editorName: displayName,
@@ -287,7 +289,7 @@ const handleBeep = (_, response) => {
287
289
  readStream.pipe(response);
288
290
  return Promise.resolve();
289
291
  };
290
- const handleRoutes = ({ staticHash, staticHashPrefix, outputHash, outputHashPrefix, request, response, liveEventsServer, getCurrentInputProps, getEnvVariables, remotionRoot, entryPoint, publicDir, logLevel, getRenderQueue, getRenderDefaults, getNumberOfAudioTags, queueMethods: methods, gitSource, binariesDirectory, getAudioLatencyHint, getExperimentalKeepAudioContextAlive, getPreviewSampleRate, enableCrossSiteIsolation, getStudioRuntimeConfig, getDefaultCodingAgent, getDefaultEditor, configFile, }) => {
292
+ const handleRoutes = ({ staticHash, staticHashPrefix, outputHash, outputHashPrefix, request, response, liveEventsServer, getCurrentInputProps, getEnvVariables, remotionRoot, entryPoint, publicDir, logLevel, getRenderQueue, getRenderDefaults, getNumberOfAudioTags, queueMethods: methods, gitSource, binariesDirectory, getAudioLatencyHint, getExperimentalKeepAudioContextAlive, getPreviewSampleRate, enableCrossSiteIsolation, getStudioRuntimeConfig, getDefaultCodingAgent, getDefaultEditor, configFile, invalidateBundle, }) => {
291
293
  const url = new URL(request.url, 'http://localhost');
292
294
  if (url.pathname === '/api/file-source') {
293
295
  return handleFileSource({
@@ -387,6 +389,22 @@ const handleRoutes = ({ staticHash, staticHashPrefix, outputHash, outputHashPref
387
389
  getDefaultEditor,
388
390
  });
389
391
  }
392
+ if (url.pathname === '/api/install-package') {
393
+ return (0, handler_1.handleRequest)({
394
+ remotionRoot,
395
+ entryPoint,
396
+ handler: (params) => (0, install_dependency_1.handleInstallPackage)({ ...params, invalidateBundle }),
397
+ request,
398
+ response,
399
+ logLevel,
400
+ methods,
401
+ binariesDirectory,
402
+ publicDir,
403
+ configFile,
404
+ getDefaultCodingAgent,
405
+ getDefaultEditor,
406
+ });
407
+ }
390
408
  for (const [key, value] of Object.entries(api_routes_1.allApiRoutes)) {
391
409
  if (url.pathname === key) {
392
410
  return (0, handler_1.handleRequest)({
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.522",
6
+ "version": "4.0.524",
7
7
  "description": "Run a Remotion Studio with a server backend",
8
8
  "main": "dist",
9
9
  "scripts": {
@@ -23,7 +23,7 @@
23
23
  "access": "public"
24
24
  },
25
25
  "dependencies": {
26
- "@remotion/studio-protocol": "4.0.522",
26
+ "@remotion/studio-protocol": "4.0.524",
27
27
  "@babel/types": "7.24.0",
28
28
  "@babel/parser": "7.24.1",
29
29
  "@svgr/core": "8.1.0",
@@ -32,12 +32,12 @@
32
32
  "semver": "7.5.3",
33
33
  "zod": "4.5.4",
34
34
  "prettier": "3.8.1",
35
- "remotion": "4.0.522",
35
+ "remotion": "4.0.524",
36
36
  "recast": "0.23.21",
37
- "@remotion/bundler": "4.0.522",
38
- "@remotion/renderer": "4.0.522",
39
- "@remotion/studio-codemods": "4.0.522",
40
- "@remotion/studio-shared": "4.0.522",
37
+ "@remotion/bundler": "4.0.524",
38
+ "@remotion/renderer": "4.0.524",
39
+ "@remotion/studio-codemods": "4.0.524",
40
+ "@remotion/studio-shared": "4.0.524",
41
41
  "memfs": "3.4.3",
42
42
  "open": "8.4.2"
43
43
  },
@@ -45,7 +45,7 @@
45
45
  "ast-types": "0.16.1",
46
46
  "react": "19.2.3",
47
47
  "@types/semver": "7.5.3",
48
- "@remotion/eslint-config-internal": "4.0.522",
48
+ "@remotion/eslint-config-internal": "4.0.524",
49
49
  "eslint": "9.19.0",
50
50
  "@types/node": "20.12.14",
51
51
  "@typescript/native-preview": "7.0.0-dev.20260217.1"