@remotion/studio-server 4.0.529 → 4.0.531

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 (109) hide show
  1. package/dist/canvas-capture/generate-canvas-capture-composition.d.ts +14 -0
  2. package/dist/canvas-capture/generate-canvas-capture-composition.js +239 -0
  3. package/dist/codemods/add-composition-to-file.d.ts +7 -0
  4. package/dist/codemods/add-composition-to-file.js +63 -0
  5. package/dist/codemods/add-effect.d.ts +1 -0
  6. package/dist/codemods/add-effect.js +8 -0
  7. package/dist/codemods/apply-codemod-to-file.js +6 -1
  8. package/dist/codemods/apply-visual-control.d.ts +8 -0
  9. package/dist/codemods/apply-visual-control.js +89 -0
  10. package/dist/codemods/delete-effect.d.ts +1 -0
  11. package/dist/codemods/delete-effect.js +6 -0
  12. package/dist/codemods/delete-jsx-node.d.ts +22 -0
  13. package/dist/codemods/delete-jsx-node.js +17 -0
  14. package/dist/codemods/duplicate-composition.d.ts +2 -0
  15. package/dist/codemods/duplicate-composition.js +64 -0
  16. package/dist/codemods/duplicate-effect.d.ts +1 -0
  17. package/dist/codemods/duplicate-effect.js +6 -0
  18. package/dist/codemods/duplicate-jsx-node.d.ts +25 -0
  19. package/dist/codemods/duplicate-jsx-node.js +26 -0
  20. package/dist/codemods/format-file-content.d.ts +7 -0
  21. package/dist/codemods/format-file-content.js +80 -0
  22. package/dist/codemods/format-inline-content.d.ts +18 -0
  23. package/dist/codemods/format-inline-content.js +78 -0
  24. package/dist/codemods/insert-video-layers.d.ts +11 -0
  25. package/dist/codemods/insert-video-layers.js +5 -0
  26. package/dist/codemods/recast-mods.d.ts +13 -0
  27. package/dist/codemods/recast-mods.js +902 -0
  28. package/dist/codemods/reorder-effect.d.ts +1 -0
  29. package/dist/codemods/reorder-effect.js +5 -0
  30. package/dist/codemods/reorder-sequence.d.ts +1 -0
  31. package/dist/codemods/reorder-sequence.js +5 -0
  32. package/dist/codemods/simple-diff.d.ts +5 -0
  33. package/dist/codemods/simple-diff.js +39 -0
  34. package/dist/codemods/split-video-from-audio.d.ts +1 -0
  35. package/dist/codemods/split-video-from-audio.js +5 -0
  36. package/dist/codemods/update-default-props.d.ts +1 -0
  37. package/dist/codemods/update-default-props.js +6 -0
  38. package/dist/codemods/update-inline-caption-patches.d.ts +1 -0
  39. package/dist/codemods/update-inline-caption-patches.js +5 -0
  40. package/dist/codemods/update-keyframes/ensure-imports-and-frame-hook.d.ts +1 -0
  41. package/dist/codemods/update-keyframes/ensure-imports-and-frame-hook.js +7 -0
  42. package/dist/codemods/update-keyframes/update-keyframes.d.ts +3 -3
  43. package/dist/codemods/update-keyframes/update-keyframes.js +1 -1
  44. package/dist/codemods/update-sequence-props/update-sequence-props.js +1 -1
  45. package/dist/helpers/get-file-source.js +6 -4
  46. package/dist/helpers/get-local-package-override.d.ts +5 -0
  47. package/dist/helpers/get-local-package-override.js +39 -0
  48. package/dist/helpers/install-command.js +1 -0
  49. package/dist/helpers/resolve-composition-component.d.ts +2 -1
  50. package/dist/helpers/resolve-composition-component.js +66 -15
  51. package/dist/index.d.ts +5 -0
  52. package/dist/index.js +3 -0
  53. package/dist/open-directory-in-finder.js +11 -9
  54. package/dist/preview-server/api-routes.js +32 -12
  55. package/dist/preview-server/element-install-state.js +10 -2
  56. package/dist/preview-server/routes/add-composition.d.ts +3 -0
  57. package/dist/preview-server/routes/add-composition.js +97 -0
  58. package/dist/preview-server/routes/add-folder.d.ts +3 -0
  59. package/dist/preview-server/routes/add-folder.js +107 -0
  60. package/dist/preview-server/routes/add-keyframes.d.ts +2 -2
  61. package/dist/preview-server/routes/add-keyframes.js +49 -6
  62. package/dist/preview-server/routes/can-update-sequence-props.js +27 -5
  63. package/dist/preview-server/routes/delete-composition.d.ts +3 -0
  64. package/dist/preview-server/routes/delete-composition.js +104 -0
  65. package/dist/preview-server/routes/delete-jsx-node.d.ts +3 -0
  66. package/dist/preview-server/routes/delete-jsx-node.js +126 -0
  67. package/dist/preview-server/routes/delete-nodes.d.ts +3 -0
  68. package/dist/preview-server/routes/delete-nodes.js +142 -0
  69. package/dist/preview-server/routes/duplicate-composition.d.ts +3 -0
  70. package/dist/preview-server/routes/duplicate-composition.js +114 -0
  71. package/dist/preview-server/routes/duplicate-nodes.d.ts +3 -0
  72. package/dist/preview-server/routes/duplicate-nodes.js +123 -0
  73. package/dist/preview-server/routes/insert-basic-captions.js +2 -1
  74. package/dist/preview-server/routes/insert-composition-element.d.ts +3 -0
  75. package/dist/preview-server/routes/insert-composition-element.js +236 -0
  76. package/dist/preview-server/routes/insert-element.js +26 -19
  77. package/dist/preview-server/routes/insert-video-layers.d.ts +3 -0
  78. package/dist/preview-server/routes/insert-video-layers.js +68 -0
  79. package/dist/preview-server/routes/install-dependency.js +10 -1
  80. package/dist/preview-server/routes/move-composition.d.ts +3 -0
  81. package/dist/preview-server/routes/move-composition.js +148 -0
  82. package/dist/preview-server/routes/move-folder.d.ts +3 -0
  83. package/dist/preview-server/routes/move-folder.js +151 -0
  84. package/dist/preview-server/routes/precompose-jsx-nodes.d.ts +3 -0
  85. package/dist/preview-server/routes/precompose-jsx-nodes.js +209 -0
  86. package/dist/preview-server/routes/rename-composition.d.ts +3 -0
  87. package/dist/preview-server/routes/rename-composition.js +105 -0
  88. package/dist/preview-server/routes/rename-folder.d.ts +3 -0
  89. package/dist/preview-server/routes/rename-folder.js +111 -0
  90. package/dist/preview-server/routes/reorder-sequence.js +7 -2
  91. package/dist/preview-server/routes/replace-video-source.js +7 -2
  92. package/dist/preview-server/routes/save-sequence-props.js +6 -3
  93. package/dist/preview-server/routes/split-sequences.d.ts +3 -0
  94. package/dist/preview-server/routes/split-sequences.js +122 -0
  95. package/dist/preview-server/routes/split-video-from-audio.js +6 -1
  96. package/dist/preview-server/routes/unwrap-folder.d.ts +3 -0
  97. package/dist/preview-server/routes/unwrap-folder.js +107 -0
  98. package/dist/preview-server/routes/update-composition-metadata.d.ts +3 -0
  99. package/dist/preview-server/routes/update-composition-metadata.js +110 -0
  100. package/dist/preview-server/routes/update-public-license.d.ts +11 -0
  101. package/dist/preview-server/routes/update-public-license.js +97 -0
  102. package/dist/preview-server/routes/wrap-node.d.ts +3 -0
  103. package/dist/preview-server/routes/wrap-node.js +108 -0
  104. package/dist/preview-server/studio-protocol/handle-discovery.js +3 -10
  105. package/dist/preview-server/studio-protocol/handle-install.js +0 -3
  106. package/dist/preview-server/undo-stack.d.ts +1 -1
  107. package/dist/preview-server/undo-stack.js +6 -0
  108. package/dist/start-studio.js +1 -1
  109. package/package.json +8 -8
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.addCompositionHandler = void 0;
4
+ const node_fs_1 = require("node:fs");
5
+ const renderer_1 = require("@remotion/renderer");
6
+ const add_composition_to_file_1 = require("../../codemods/add-composition-to-file");
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 project_info_1 = require("../project-info");
11
+ const undo_stack_1 = require("../undo-stack");
12
+ const can_update_default_props_1 = require("./can-update-default-props");
13
+ const source_file_write_queue_1 = require("./source-file-write-queue");
14
+ const addCompositionHandler = ({ input: request, logLevel, remotionRoot, entryPoint }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
15
+ var _a, _b;
16
+ var _c, _d;
17
+ try {
18
+ let filePath;
19
+ if ((_a = request.symbolicatedStack) === null || _a === void 0 ? void 0 : _a.originalFileName) {
20
+ filePath = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
21
+ remotionRoot,
22
+ fileName: request.symbolicatedStack.originalFileName,
23
+ action: 'add a composition to',
24
+ }).absolutePath;
25
+ }
26
+ else {
27
+ const { rootFile } = await (0, project_info_1.getProjectInfo)(remotionRoot, entryPoint);
28
+ if (rootFile === null) {
29
+ throw new Error('Cannot find the Remotion root file');
30
+ }
31
+ filePath = rootFile;
32
+ }
33
+ (0, can_update_default_props_1.checkIfTypeScriptFile)(filePath);
34
+ const result = await (0, add_composition_to_file_1.addCompositionToFile)({
35
+ filePath,
36
+ options: request.options,
37
+ remotionRoot,
38
+ });
39
+ for (const change of result.changes) {
40
+ const currentContents = (0, node_fs_1.existsSync)(change.filePath)
41
+ ? (0, node_fs_1.readFileSync)(change.filePath, 'utf-8')
42
+ : null;
43
+ if (currentContents !== change.previousContents) {
44
+ throw new Error(`Source changed before adding composition: ${change.filePath}`);
45
+ }
46
+ }
47
+ if (result.changes.length > 0) {
48
+ const logLine = (_c = (_b = request.symbolicatedStack) === null || _b === void 0 ? void 0 : _b.originalLineNumber) !== null && _c !== void 0 ? _c : 1;
49
+ (0, undo_stack_1.pushTransactionToUndoStack)({
50
+ snapshots: result.changes.map((change) => ({
51
+ filePath: change.filePath,
52
+ oldContents: change.previousContents,
53
+ newContents: change.nextContents,
54
+ logLine: change.filePath === filePath ? logLine : 1,
55
+ nodePathRemappings: null,
56
+ })),
57
+ logLevel,
58
+ remotionRoot,
59
+ description: {
60
+ undoMessage: `↩️ Creation of composition "${request.options.newId}"`,
61
+ redoMessage: `↪️ Creation of composition "${request.options.newId}"`,
62
+ },
63
+ entryType: 'new-composition',
64
+ suppressHmrOnFileRestore: false,
65
+ undoRedoNavigation: request.undoRedoNavigation,
66
+ });
67
+ for (const change of result.changes) {
68
+ if (change.nextContents === null) {
69
+ throw new Error('Adding a composition cannot remove source files');
70
+ }
71
+ (0, undo_stack_1.suppressUndoStackInvalidation)(change.filePath);
72
+ (0, file_watcher_1.writeFileAndNotifyFileWatchers)({
73
+ file: change.filePath,
74
+ content: change.nextContents,
75
+ originatorClientId: undefined,
76
+ metadata: null,
77
+ });
78
+ }
79
+ const locationLabel = (0, format_log_file_location_1.formatLogFileLocation)({
80
+ remotionRoot,
81
+ absolutePath: filePath,
82
+ line: logLine,
83
+ });
84
+ renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, `${(0, source_file_write_queue_1.getCodemodTimingPrefix)(logLevel)}${renderer_1.RenderInternals.chalk.blueBright(locationLabel)} Created composition "${request.options.newId}"`);
85
+ (0, undo_stack_1.printUndoHint)(logLevel);
86
+ }
87
+ return { success: true, nodePathMutation: null };
88
+ }
89
+ catch (error) {
90
+ return {
91
+ success: false,
92
+ reason: error instanceof Error ? error.message : String(error),
93
+ stack: error instanceof Error ? ((_d = error.stack) !== null && _d !== void 0 ? _d : '') : '',
94
+ };
95
+ }
96
+ });
97
+ exports.addCompositionHandler = addCompositionHandler;
@@ -0,0 +1,3 @@
1
+ import type { AddFolderRequest, CompositionEditResponse } from '@remotion/studio-shared';
2
+ import type { ApiHandler } from '../api-types';
3
+ export declare const addFolderHandler: ApiHandler<AddFolderRequest, CompositionEditResponse>;
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.addFolderHandler = void 0;
7
+ const node_fs_1 = require("node:fs");
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const codemods_1 = require("@remotion/codemods");
10
+ const renderer_1 = require("@remotion/renderer");
11
+ const file_watcher_1 = require("../../file-watcher");
12
+ const resolve_file_inside_project_1 = require("../../helpers/resolve-file-inside-project");
13
+ const format_log_file_location_1 = require("../format-log-file-location");
14
+ const project_info_1 = require("../project-info");
15
+ const undo_stack_1 = require("../undo-stack");
16
+ const can_update_default_props_1 = require("./can-update-default-props");
17
+ const source_file_write_queue_1 = require("./source-file-write-queue");
18
+ const addFolderHandler = ({ input: request, logLevel, remotionRoot, entryPoint }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
19
+ var _a, _b;
20
+ var _c, _d;
21
+ try {
22
+ let filePath;
23
+ if ((_a = request.symbolicatedStack) === null || _a === void 0 ? void 0 : _a.originalFileName) {
24
+ filePath = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
25
+ remotionRoot,
26
+ fileName: request.symbolicatedStack.originalFileName,
27
+ action: 'add a folder to',
28
+ }).absolutePath;
29
+ }
30
+ else {
31
+ const { rootFile } = await (0, project_info_1.getProjectInfo)(remotionRoot, entryPoint);
32
+ if (rootFile === null) {
33
+ throw new Error('Cannot find the Remotion root file');
34
+ }
35
+ filePath = rootFile;
36
+ }
37
+ (0, can_update_default_props_1.checkIfTypeScriptFile)(filePath);
38
+ const result = (0, codemods_1.addFolder)({
39
+ project: {
40
+ files: { [filePath]: (0, node_fs_1.readFileSync)(filePath, 'utf-8') },
41
+ rootDir: node_path_1.default.dirname(filePath),
42
+ },
43
+ compositionFile: filePath,
44
+ folder: { name: request.folderName, parentName: request.parentName },
45
+ });
46
+ for (const change of result.changes) {
47
+ const currentContents = (0, node_fs_1.existsSync)(change.filePath)
48
+ ? (0, node_fs_1.readFileSync)(change.filePath, 'utf-8')
49
+ : null;
50
+ if (currentContents !== change.previousContents) {
51
+ throw new Error(`Source changed before adding folder: ${change.filePath}`);
52
+ }
53
+ }
54
+ if (result.changes.length > 0) {
55
+ const logLine = (_c = (_b = request.symbolicatedStack) === null || _b === void 0 ? void 0 : _b.originalLineNumber) !== null && _c !== void 0 ? _c : 1;
56
+ const folderPath = request.parentName
57
+ ? `${request.parentName}/${request.folderName}`
58
+ : request.folderName;
59
+ (0, undo_stack_1.pushTransactionToUndoStack)({
60
+ snapshots: result.changes.map((change) => ({
61
+ filePath: change.filePath,
62
+ oldContents: change.previousContents,
63
+ newContents: change.nextContents,
64
+ logLine: change.filePath === filePath ? logLine : 1,
65
+ nodePathRemappings: null,
66
+ })),
67
+ logLevel,
68
+ remotionRoot,
69
+ description: {
70
+ undoMessage: `↩️ Creation of folder "${folderPath}"`,
71
+ redoMessage: `↪️ Creation of folder "${folderPath}"`,
72
+ },
73
+ entryType: 'new-folder',
74
+ suppressHmrOnFileRestore: false,
75
+ undoRedoNavigation: request.undoRedoNavigation,
76
+ });
77
+ for (const change of result.changes) {
78
+ if (change.nextContents === null) {
79
+ throw new Error('Adding a folder cannot remove source files');
80
+ }
81
+ (0, undo_stack_1.suppressUndoStackInvalidation)(change.filePath);
82
+ (0, file_watcher_1.writeFileAndNotifyFileWatchers)({
83
+ file: change.filePath,
84
+ content: change.nextContents,
85
+ originatorClientId: undefined,
86
+ metadata: null,
87
+ });
88
+ }
89
+ const locationLabel = (0, format_log_file_location_1.formatLogFileLocation)({
90
+ remotionRoot,
91
+ absolutePath: filePath,
92
+ line: logLine,
93
+ });
94
+ renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, `${(0, source_file_write_queue_1.getCodemodTimingPrefix)(logLevel)}${renderer_1.RenderInternals.chalk.blueBright(locationLabel)} Created folder "${folderPath}"`);
95
+ (0, undo_stack_1.printUndoHint)(logLevel);
96
+ }
97
+ return { success: true, nodePathMutation: null };
98
+ }
99
+ catch (error) {
100
+ return {
101
+ success: false,
102
+ reason: error instanceof Error ? error.message : String(error),
103
+ stack: error instanceof Error ? ((_d = error.stack) !== null && _d !== void 0 ? _d : '') : '',
104
+ };
105
+ }
106
+ });
107
+ exports.addFolderHandler = addFolderHandler;
@@ -1,7 +1,7 @@
1
- import type { AddKeyframesRequest, AddKeyframesResponse } from '@remotion/studio-shared';
1
+ import type { AddKeyframesRequest, AddKeyframesResponse, SequenceNodePathMutation } from '@remotion/studio-shared';
2
2
  import type { ApiHandler } from '../api-types';
3
3
  export declare const addKeyframes: ({ sequenceKeyframes, effectKeyframes, clientId, remotionRoot, logLevel, }: AddKeyframesRequest & {
4
4
  readonly remotionRoot: string;
5
5
  readonly logLevel: "error" | "info" | "trace" | "verbose" | "warn";
6
- }) => Promise<void>;
6
+ }) => Promise<SequenceNodePathMutation | null>;
7
7
  export declare const addKeyframesHandler: ApiHandler<AddKeyframesRequest, AddKeyframesResponse>;
@@ -3,9 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.addKeyframesHandler = exports.addKeyframes = void 0;
4
4
  const node_fs_1 = require("node:fs");
5
5
  const renderer_1 = require("@remotion/renderer");
6
+ const get_node_path_remappings_1 = require("../../codemods/get-node-path-remappings");
7
+ const parse_ast_1 = require("../../codemods/parse-ast");
6
8
  const update_keyframes_1 = require("../../codemods/update-keyframes/update-keyframes");
7
9
  const file_watcher_1 = require("../../file-watcher");
8
10
  const resolve_file_inside_project_1 = require("../../helpers/resolve-file-inside-project");
11
+ const sequence_node_path_mutation_1 = require("../sequence-node-path-mutation");
9
12
  const undo_stack_1 = require("../undo-stack");
10
13
  const watch_ignore_next_change_1 = require("../watch-ignore-next-change");
11
14
  const log_effect_update_1 = require("./log-updates/log-effect-update");
@@ -86,6 +89,20 @@ const addKeyframes = async ({ sequenceKeyframes, effectKeyframes, clientId, remo
86
89
  const fileContents = (0, node_fs_1.readFileSync)(absolutePath, 'utf-8');
87
90
  let output = fileContents;
88
91
  let firstLogLine = Number.POSITIVE_INFINITY;
92
+ const originalNodes = (0, get_node_path_remappings_1.captureJsxNodePaths)((0, parse_ast_1.parseAst)(fileContents));
93
+ // Keyframe codemods may wrap a concise arrow in a return statement.
94
+ // JSX nodes remain in order, but their node paths can all change.
95
+ const currentPathFor = (originalPath) => {
96
+ const index = originalNodes.findIndex(({ nodePath }) => JSON.stringify(nodePath) === JSON.stringify(originalPath));
97
+ if (index === -1) {
98
+ throw new Error('Could not find the original JSX node for keyframing');
99
+ }
100
+ const currentNodes = (0, get_node_path_remappings_1.captureJsxNodePaths)((0, parse_ast_1.parseAst)(output));
101
+ if (currentNodes.length !== originalNodes.length) {
102
+ throw new Error('Could not remap JSX nodes after keyframing');
103
+ }
104
+ return currentNodes[index].nodePath;
105
+ };
89
106
  for (const keyframeGroup of groupBy(group.sequenceKeyframes, (keyframe) => JSON.stringify(keyframe.nodePath.nodePath))) {
90
107
  const [firstSequenceKeyframe] = keyframeGroup;
91
108
  if (!firstSequenceKeyframe) {
@@ -93,7 +110,7 @@ const addKeyframes = async ({ sequenceKeyframes, effectKeyframes, clientId, remo
93
110
  }
94
111
  const result = await (0, update_keyframes_1.updateSequenceKeyframes)({
95
112
  input: output,
96
- nodePath: firstSequenceKeyframe.nodePath.nodePath,
113
+ nodePath: currentPathFor(firstSequenceKeyframe.nodePath.nodePath),
97
114
  schema: firstSequenceKeyframe.schema,
98
115
  videoConfigValues: firstSequenceKeyframe.nodePath.videoConfigValues,
99
116
  updates: keyframeGroup.map((keyframe) => ({
@@ -125,7 +142,7 @@ const addKeyframes = async ({ sequenceKeyframes, effectKeyframes, clientId, remo
125
142
  }
126
143
  const result = await (0, update_keyframes_1.updateEffectKeyframes)({
127
144
  input: output,
128
- sequenceNodePath: firstEffectKeyframe.sequenceNodePath.nodePath,
145
+ sequenceNodePath: currentPathFor(firstEffectKeyframe.sequenceNodePath.nodePath),
129
146
  effectIndex: firstEffectKeyframe.effectIndex,
130
147
  schema: firstEffectKeyframe.schema,
131
148
  videoConfigValues: firstEffectKeyframe.sequenceNodePath.videoConfigValues,
@@ -152,21 +169,39 @@ const addKeyframes = async ({ sequenceKeyframes, effectKeyframes, clientId, remo
152
169
  });
153
170
  }
154
171
  }
172
+ const finalNodes = (0, get_node_path_remappings_1.captureJsxNodePaths)((0, parse_ast_1.parseAst)(output));
173
+ if (finalNodes.length !== originalNodes.length) {
174
+ throw new Error('Could not remap JSX nodes after keyframing');
175
+ }
155
176
  snapshots.push({
156
177
  filePath: absolutePath,
157
178
  oldContents: fileContents,
158
179
  newContents: output,
159
180
  logLine: Number.isFinite(firstLogLine) ? firstLogLine : 1,
181
+ nodePathRemappings: originalNodes.flatMap(({ nodePath }, index) => {
182
+ const newNodePath = finalNodes[index].nodePath;
183
+ return JSON.stringify(nodePath) === JSON.stringify(newNodePath)
184
+ ? []
185
+ : [{ oldNodePath: nodePath, newNodePath }];
186
+ }),
160
187
  });
161
188
  }
162
189
  const [firstKeyframe] = sequenceKeyframes.length > 0 ? sequenceKeyframes : effectKeyframes;
163
190
  if (!firstKeyframe) {
164
191
  throw new Error('No keyframes were specified for adding');
165
192
  }
193
+ const filesWithRemappings = snapshots
194
+ .filter((snapshot) => snapshot.nodePathRemappings.length > 0)
195
+ .map((snapshot) => ({
196
+ absolutePath: snapshot.filePath,
197
+ remappings: snapshot.nodePathRemappings,
198
+ }));
166
199
  (0, undo_stack_1.pushTransactionToUndoStack)({
167
200
  snapshots: snapshots.map((snapshot) => ({
168
201
  ...snapshot,
169
- nodePathRemappings: null,
202
+ nodePathRemappings: snapshot.nodePathRemappings.length
203
+ ? snapshot.nodePathRemappings
204
+ : null,
170
205
  })),
171
206
  logLevel,
172
207
  remotionRoot,
@@ -181,6 +216,8 @@ const addKeyframes = async ({ sequenceKeyframes, effectKeyframes, clientId, remo
181
216
  suppressHmrOnFileRestore: true,
182
217
  undoRedoNavigation: null,
183
218
  });
219
+ // Visual Mode evaluates keyframes from prop statuses. Remap and refresh the
220
+ // subscriptions instead of remounting the composition when a hook is added.
184
221
  for (const snapshot of snapshots) {
185
222
  (0, undo_stack_1.suppressUndoStackInvalidation)(snapshot.filePath);
186
223
  (0, watch_ignore_next_change_1.suppressBundlerUpdateForFile)(snapshot.filePath);
@@ -188,9 +225,14 @@ const addKeyframes = async ({ sequenceKeyframes, effectKeyframes, clientId, remo
188
225
  file: snapshot.filePath,
189
226
  content: snapshot.newContents,
190
227
  originatorClientId: clientId,
191
- metadata: null,
228
+ metadata: snapshot.nodePathRemappings.length
229
+ ? { skipSequencePropsUpdate: true }
230
+ : null,
192
231
  });
193
232
  }
233
+ const nodePathMutation = filesWithRemappings.length
234
+ ? (0, sequence_node_path_mutation_1.broadcastSequenceNodePathMutation)(filesWithRemappings, null)
235
+ : null;
194
236
  for (const log of sequenceLogs) {
195
237
  (0, log_update_1.logUpdate)({
196
238
  fileRelativeToRoot: log.fileRelativeToRoot,
@@ -221,17 +263,18 @@ const addKeyframes = async ({ sequenceKeyframes, effectKeyframes, clientId, remo
221
263
  });
222
264
  }
223
265
  (0, undo_stack_1.printUndoHint)(logLevel);
266
+ return nodePathMutation;
224
267
  };
225
268
  exports.addKeyframes = addKeyframes;
226
269
  const addKeyframesHandler = ({ input: { sequenceKeyframes, effectKeyframes, clientId }, remotionRoot, logLevel, }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
227
270
  renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[add-keyframes] Received request to add ${sequenceKeyframes.length + effectKeyframes.length} keyframe(s)`);
228
- await (0, exports.addKeyframes)({
271
+ const nodePathMutation = await (0, exports.addKeyframes)({
229
272
  sequenceKeyframes,
230
273
  effectKeyframes,
231
274
  clientId,
232
275
  remotionRoot,
233
276
  logLevel,
234
277
  });
235
- return { success: true };
278
+ return { success: true, nodePathMutation };
236
279
  });
237
280
  exports.addKeyframesHandler = addKeyframesHandler;
@@ -458,6 +458,7 @@ const getInterpolationKeyframes = (node, ast, videoConfigValues) => {
458
458
  clamping: metadata.clamping,
459
459
  posterize: metadata.posterize,
460
460
  output: metadata.output,
461
+ keyframePlaybackRateAdjustment: frameDisplayOffset.playbackRateAdjustment,
461
462
  keyframeDisplayOffsetAdjustment: frameDisplayOffset.hasEnclosingElement
462
463
  ? frameDisplayOffset.adjustment
463
464
  : null,
@@ -548,6 +549,7 @@ const getFrameDisplayOffsetAdjustmentBetweenPaths = ({ startPath, endPath, video
548
549
  let hasSeenControlledElement = false;
549
550
  let hasEnclosingElement = false;
550
551
  let adjustment = 0;
552
+ let playbackRateAdjustment = 1;
551
553
  while (current && current.value !== endPath.value) {
552
554
  const currentNode = current.value;
553
555
  if (currentNode.type === 'FunctionDeclaration' ||
@@ -575,15 +577,30 @@ const getFrameDisplayOffsetAdjustmentBetweenPaths = ({ startPath, endPath, video
575
577
  defaultValue: 0,
576
578
  videoConfigValues,
577
579
  });
578
- if (from === null || trimBefore === null) {
580
+ const playbackRate = getJsxNumericAttribute({
581
+ openingElement: currentNode.openingElement,
582
+ name: 'playbackRate',
583
+ defaultValue: 1,
584
+ videoConfigValues,
585
+ });
586
+ if (from === null ||
587
+ trimBefore === null ||
588
+ playbackRate === null ||
589
+ !Number.isFinite(playbackRate) ||
590
+ playbackRate <= 0) {
579
591
  return null;
580
592
  }
581
- adjustment -= from - trimBefore;
593
+ // Walk from the controlled element back to the hook's scope.
594
+ // A sequence maps parent time to (parent - from) * rate + trimBefore.
595
+ adjustment = (adjustment + trimBefore) / playbackRate - from;
596
+ playbackRateAdjustment /= playbackRate;
582
597
  }
583
598
  }
584
599
  current = current.parentPath;
585
600
  }
586
- return current ? { adjustment, hasEnclosingElement } : null;
601
+ return current
602
+ ? { adjustment, playbackRateAdjustment, hasEnclosingElement }
603
+ : null;
587
604
  };
588
605
  const resolveCurrentFrameExpression = ({ node, ast, seenDeclarations, }) => {
589
606
  var _a;
@@ -723,6 +740,11 @@ const getComputedStatus = (node, ast, videoConfigValues) => {
723
740
  status: 'keyframed',
724
741
  interpolationFunction: interpolation.interpolationFunction,
725
742
  keyframeDisplayOffsetAdjustment: interpolation.keyframeDisplayOffsetAdjustment,
743
+ ...(interpolation.keyframePlaybackRateAdjustment === 1
744
+ ? {}
745
+ : {
746
+ keyframePlaybackRateAdjustment: interpolation.keyframePlaybackRateAdjustment,
747
+ }),
726
748
  keyframes: interpolation.keyframes,
727
749
  easing: interpolation.easing,
728
750
  clamping: interpolation.clamping,
@@ -934,7 +956,7 @@ const resolveSequencePropsNodePathsFromFilename = ({ fileName, targets, remotion
934
956
  action: 'read',
935
957
  });
936
958
  const fileContents = (0, node_fs_1.readFileSync)(absolutePath, 'utf-8');
937
- const nodes = (0, codemods_1.getJsxNodes)({
959
+ const nodes = (0, codemods_1.getNodes)({
938
960
  project: { rootDir: remotionRoot, files: { [absolutePath]: fileContents } },
939
961
  filePath: absolutePath,
940
962
  });
@@ -955,7 +977,7 @@ const resolveSequencePropsNodePathsFromFilename = ({ fileName, targets, remotion
955
977
  exports.resolveSequencePropsNodePathsFromFilename = resolveSequencePropsNodePathsFromFilename;
956
978
  const computeSequencePropsStatusFromContent = ({ fileContents, nodePath, componentIdentity, keys, assetKeys = [], effects, videoConfigValues, }) => {
957
979
  try {
958
- return (0, codemods_1.getJsxNodeProps)({
980
+ return (0, codemods_1.getNodeProps)({
959
981
  project: { rootDir: '/', files: { 'source.tsx': fileContents } },
960
982
  node: { filePath: 'source.tsx', nodePath },
961
983
  componentIdentity,
@@ -0,0 +1,3 @@
1
+ import type { CompositionEditResponse, DeleteCompositionRequest } from '@remotion/studio-shared';
2
+ import type { ApiHandler } from '../api-types';
3
+ export declare const deleteCompositionHandler: ApiHandler<DeleteCompositionRequest, CompositionEditResponse>;
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.deleteCompositionHandler = void 0;
7
+ const node_fs_1 = require("node:fs");
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const codemods_1 = require("@remotion/codemods");
10
+ const renderer_1 = require("@remotion/renderer");
11
+ const file_watcher_1 = require("../../file-watcher");
12
+ const resolve_file_inside_project_1 = require("../../helpers/resolve-file-inside-project");
13
+ const format_log_file_location_1 = require("../format-log-file-location");
14
+ const project_info_1 = require("../project-info");
15
+ const undo_stack_1 = require("../undo-stack");
16
+ const can_update_default_props_1 = require("./can-update-default-props");
17
+ const source_file_write_queue_1 = require("./source-file-write-queue");
18
+ const deleteCompositionHandler = ({ input: request, logLevel, remotionRoot, entryPoint }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
19
+ var _a, _b;
20
+ var _c, _d;
21
+ try {
22
+ let filePath;
23
+ if ((_a = request.symbolicatedStack) === null || _a === void 0 ? void 0 : _a.originalFileName) {
24
+ filePath = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
25
+ remotionRoot,
26
+ fileName: request.symbolicatedStack.originalFileName,
27
+ action: 'delete a composition from',
28
+ }).absolutePath;
29
+ }
30
+ else {
31
+ const { rootFile } = await (0, project_info_1.getProjectInfo)(remotionRoot, entryPoint);
32
+ if (rootFile === null) {
33
+ throw new Error('Cannot find the Remotion root file');
34
+ }
35
+ filePath = rootFile;
36
+ }
37
+ (0, can_update_default_props_1.checkIfTypeScriptFile)(filePath);
38
+ const result = (0, codemods_1.deleteComposition)({
39
+ project: {
40
+ files: { [filePath]: (0, node_fs_1.readFileSync)(filePath, 'utf-8') },
41
+ rootDir: node_path_1.default.dirname(filePath),
42
+ },
43
+ compositionFile: filePath,
44
+ compositionId: request.idToDelete,
45
+ });
46
+ for (const change of result.changes) {
47
+ const currentContents = (0, node_fs_1.existsSync)(change.filePath)
48
+ ? (0, node_fs_1.readFileSync)(change.filePath, 'utf-8')
49
+ : null;
50
+ if (currentContents !== change.previousContents) {
51
+ throw new Error(`Source changed before deleting composition: ${change.filePath}`);
52
+ }
53
+ }
54
+ if (result.changes.length > 0) {
55
+ const logLine = (_c = (_b = request.symbolicatedStack) === null || _b === void 0 ? void 0 : _b.originalLineNumber) !== null && _c !== void 0 ? _c : 1;
56
+ (0, undo_stack_1.pushTransactionToUndoStack)({
57
+ snapshots: result.changes.map((change) => ({
58
+ filePath: change.filePath,
59
+ oldContents: change.previousContents,
60
+ newContents: change.nextContents,
61
+ logLine: change.filePath === filePath ? logLine : 1,
62
+ nodePathRemappings: null,
63
+ })),
64
+ logLevel,
65
+ remotionRoot,
66
+ description: {
67
+ undoMessage: `↩️ Deletion of composition "${request.idToDelete}"`,
68
+ redoMessage: `↪️ Deletion of composition "${request.idToDelete}"`,
69
+ },
70
+ entryType: 'delete-composition',
71
+ suppressHmrOnFileRestore: false,
72
+ undoRedoNavigation: request.undoRedoNavigation,
73
+ });
74
+ for (const change of result.changes) {
75
+ if (change.nextContents === null) {
76
+ throw new Error('Deleting a composition cannot remove source files');
77
+ }
78
+ (0, undo_stack_1.suppressUndoStackInvalidation)(change.filePath);
79
+ (0, file_watcher_1.writeFileAndNotifyFileWatchers)({
80
+ file: change.filePath,
81
+ content: change.nextContents,
82
+ originatorClientId: undefined,
83
+ metadata: null,
84
+ });
85
+ }
86
+ const locationLabel = (0, format_log_file_location_1.formatLogFileLocation)({
87
+ remotionRoot,
88
+ absolutePath: filePath,
89
+ line: logLine,
90
+ });
91
+ renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, `${(0, source_file_write_queue_1.getCodemodTimingPrefix)(logLevel)}${renderer_1.RenderInternals.chalk.blueBright(locationLabel)} Deleted composition "${request.idToDelete}"`);
92
+ (0, undo_stack_1.printUndoHint)(logLevel);
93
+ }
94
+ return { success: true, nodePathMutation: null };
95
+ }
96
+ catch (error) {
97
+ return {
98
+ success: false,
99
+ reason: error instanceof Error ? error.message : String(error),
100
+ stack: error instanceof Error ? ((_d = error.stack) !== null && _d !== void 0 ? _d : '') : '',
101
+ };
102
+ }
103
+ });
104
+ exports.deleteCompositionHandler = deleteCompositionHandler;
@@ -0,0 +1,3 @@
1
+ import type { DeleteJsxNodeRequest, DeleteJsxNodeResponse } from '@remotion/studio-shared';
2
+ import type { ApiHandler } from '../api-types';
3
+ export declare const deleteJsxNodeHandler: ApiHandler<DeleteJsxNodeRequest, DeleteJsxNodeResponse>;