@remotion/studio-server 4.0.507 → 4.0.509

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 (76) 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 +174 -0
  3. package/dist/codemods/delete-jsx-node.d.ts +4 -0
  4. package/dist/codemods/delete-jsx-node.js +8 -0
  5. package/dist/codemods/duplicate-composition.js +8 -6
  6. package/dist/codemods/duplicate-jsx-node.d.ts +2 -0
  7. package/dist/codemods/duplicate-jsx-node.js +8 -0
  8. package/dist/codemods/get-node-path-remappings.d.ts +16 -0
  9. package/dist/codemods/get-node-path-remappings.js +88 -0
  10. package/dist/codemods/recast-mods.js +13 -0
  11. package/dist/codemods/reorder-sequence.d.ts +2 -1
  12. package/dist/codemods/reorder-sequence.js +8 -0
  13. package/dist/codemods/split-jsx-sequence.d.ts +2 -0
  14. package/dist/codemods/split-jsx-sequence.js +8 -0
  15. package/dist/codemods/update-keyframes/update-keyframes.js +17 -10
  16. package/dist/file-watcher.d.ts +11 -1
  17. package/dist/file-watcher.js +16 -5
  18. package/dist/helpers/coding-agent-registry.d.ts +0 -1
  19. package/dist/helpers/coding-agent-registry.js +1 -5
  20. package/dist/helpers/finder-artwork.d.ts +1 -0
  21. package/dist/helpers/finder-artwork.js +4 -0
  22. package/dist/helpers/resolve-composition-component.d.ts +4 -1
  23. package/dist/helpers/resolve-composition-component.js +63 -69
  24. package/dist/helpers/terminal-artwork.d.ts +6 -0
  25. package/dist/helpers/terminal-artwork.js +14 -0
  26. package/dist/helpers/terminal-registry.d.ts +36 -0
  27. package/dist/helpers/terminal-registry.js +401 -0
  28. package/dist/index.d.ts +8 -2
  29. package/dist/preview-server/api-routes.js +2 -0
  30. package/dist/preview-server/live-events.d.ts +1 -0
  31. package/dist/preview-server/live-events.js +3 -1
  32. package/dist/preview-server/routes/add-effect-keyframe.js +7 -1
  33. package/dist/preview-server/routes/add-effect.js +7 -1
  34. package/dist/preview-server/routes/add-keyframes.js +10 -2
  35. package/dist/preview-server/routes/add-sequence-keyframe.js +7 -1
  36. package/dist/preview-server/routes/app-icon.d.ts +6 -0
  37. package/dist/preview-server/routes/app-icon.js +66 -0
  38. package/dist/preview-server/routes/apply-codemod.js +34 -5
  39. package/dist/preview-server/routes/apply-visual-control-change.js +7 -1
  40. package/dist/preview-server/routes/batch-update-keyframe-settings.js +10 -2
  41. package/dist/preview-server/routes/can-update-sequence-props.d.ts +1 -0
  42. package/dist/preview-server/routes/can-update-sequence-props.js +5 -4
  43. package/dist/preview-server/routes/default-coding-agent.js +4 -2
  44. package/dist/preview-server/routes/delete-effect.js +7 -1
  45. package/dist/preview-server/routes/delete-jsx-node.js +16 -2
  46. package/dist/preview-server/routes/delete-keyframes.js +10 -2
  47. package/dist/preview-server/routes/duplicate-effect.js +7 -1
  48. package/dist/preview-server/routes/duplicate-jsx-node.js +17 -5
  49. package/dist/preview-server/routes/insert-element.js +23 -3
  50. package/dist/preview-server/routes/insert-jsx-element.js +23 -2
  51. package/dist/preview-server/routes/move-keyframes.js +10 -2
  52. package/dist/preview-server/routes/open-in-terminal.d.ts +3 -0
  53. package/dist/preview-server/routes/open-in-terminal.js +17 -0
  54. package/dist/preview-server/routes/paste-effects.js +7 -1
  55. package/dist/preview-server/routes/reorder-effect.js +7 -1
  56. package/dist/preview-server/routes/reorder-sequence.js +17 -2
  57. package/dist/preview-server/routes/save-effect-props.js +7 -1
  58. package/dist/preview-server/routes/save-multiple-effect-props.js +10 -2
  59. package/dist/preview-server/routes/save-sequence-props.js +29 -5
  60. package/dist/preview-server/routes/split-jsx-sequence.js +17 -6
  61. package/dist/preview-server/routes/update-config.js +6 -1
  62. package/dist/preview-server/routes/update-default-props.js +7 -1
  63. package/dist/preview-server/routes/update-effect-keyframe-settings.js +7 -1
  64. package/dist/preview-server/routes/update-sequence-keyframe-settings.js +7 -1
  65. package/dist/preview-server/sequence-node-path-mutation.d.ts +7 -0
  66. package/dist/preview-server/sequence-node-path-mutation.js +21 -0
  67. package/dist/preview-server/sequence-props-watchers.js +15 -6
  68. package/dist/preview-server/start-server.d.ts +1 -0
  69. package/dist/preview-server/start-server.js +1 -0
  70. package/dist/preview-server/undo-stack.d.ts +7 -13
  71. package/dist/preview-server/undo-stack.js +58 -5
  72. package/dist/routes.d.ts +2 -1
  73. package/dist/routes.js +12 -2
  74. package/dist/start-studio.d.ts +2 -1
  75. package/dist/start-studio.js +2 -1
  76. package/package.json +8 -8
@@ -91,10 +91,16 @@ const saveEffectPropsHandler = ({ input, remotionRoot, logLevel }) => (0, source
91
91
  },
92
92
  entryType: 'effect-props',
93
93
  suppressHmrOnFileRestore: true,
94
+ nodePathRemappings: null,
94
95
  });
95
96
  (0, undo_stack_1.suppressUndoStackInvalidation)(absolutePath);
96
97
  (0, watch_ignore_next_change_1.suppressBundlerUpdateForFile)(absolutePath);
97
- (0, file_watcher_1.writeFileAndNotifyFileWatchers)(absolutePath, output, clientId);
98
+ (0, file_watcher_1.writeFileAndNotifyFileWatchers)({
99
+ file: absolutePath,
100
+ content: output,
101
+ originatorClientId: clientId,
102
+ metadata: null,
103
+ });
98
104
  (0, log_effect_update_1.logEffectUpdate)({
99
105
  fileRelativeToRoot,
100
106
  line: logLine,
@@ -87,7 +87,10 @@ const saveMultipleEffectPropsHandler = ({ input: { edits, clientId, undoLabel, r
87
87
  });
88
88
  }
89
89
  (0, undo_stack_1.pushTransactionToUndoStack)({
90
- snapshots,
90
+ snapshots: snapshots.map((snapshot) => ({
91
+ ...snapshot,
92
+ nodePathRemappings: null,
93
+ })),
91
94
  logLevel,
92
95
  remotionRoot,
93
96
  description: {
@@ -100,7 +103,12 @@ const saveMultipleEffectPropsHandler = ({ input: { edits, clientId, undoLabel, r
100
103
  for (const [absolutePath, output] of outputByPath) {
101
104
  (0, undo_stack_1.suppressUndoStackInvalidation)(absolutePath);
102
105
  (0, watch_ignore_next_change_1.suppressBundlerUpdateForFile)(absolutePath);
103
- (0, file_watcher_1.writeFileAndNotifyFileWatchers)(absolutePath, output, clientId);
106
+ (0, file_watcher_1.writeFileAndNotifyFileWatchers)({
107
+ file: absolutePath,
108
+ content: output,
109
+ originatorClientId: clientId,
110
+ metadata: null,
111
+ });
104
112
  }
105
113
  for (const index of edits.keys()) {
106
114
  const result = resultByIndex.get(index);
@@ -27,6 +27,15 @@ const stringifySequencePropEditValue = (value) => {
27
27
  }
28
28
  return JSON.stringify(value);
29
29
  };
30
+ const stringifySequencePropSourceEdit = (sourceEdit, fallbackValue) => {
31
+ if ((sourceEdit === null || sourceEdit === void 0 ? void 0 : sourceEdit.type) !== 'clipboard-param') {
32
+ return stringifySequencePropEditValue(fallbackValue);
33
+ }
34
+ if (sourceEdit.param.type === 'static') {
35
+ return stringifySequencePropEditValue(sourceEdit.param.value);
36
+ }
37
+ return `${sourceEdit.param.interpolationFunction}(frame, ${JSON.stringify(sourceEdit.param.keyframes.map((keyframe) => keyframe.frame))}, ${JSON.stringify(sourceEdit.param.keyframes.map((keyframe) => keyframe.value))})`;
38
+ };
30
39
  const groupBy = (items, getKey) => {
31
40
  var _a;
32
41
  const groups = new Map();
@@ -39,7 +48,7 @@ const groupBy = (items, getKey) => {
39
48
  return [...groups.values()];
40
49
  };
41
50
  const convertSequencePropEditToCodemodChange = (edit) => {
42
- var _a;
51
+ var _a, _b;
43
52
  return {
44
53
  nodePath: edit.nodePath.nodePath,
45
54
  updates: [
@@ -48,6 +57,9 @@ const convertSequencePropEditToCodemodChange = (edit) => {
48
57
  value: edit.value,
49
58
  defaultValue: edit.defaultValue,
50
59
  googleFont: ((_a = edit.sourceEdit) === null || _a === void 0 ? void 0 : _a.type) === 'google-font' ? edit.sourceEdit.font : null,
60
+ clipboardParam: ((_b = edit.sourceEdit) === null || _b === void 0 ? void 0 : _b.type) === 'clipboard-param'
61
+ ? edit.sourceEdit.param
62
+ : null,
51
63
  },
52
64
  ],
53
65
  schema: edit.schema,
@@ -98,7 +110,7 @@ const saveSequencePropsHandler = ({ input: { edits, addedKeyframes, movedKeyfram
98
110
  nodePath: edit.nodePath,
99
111
  key: edit.key,
100
112
  value: parsedValue,
101
- valueString: stringifySequencePropEditValue(parsedValue),
113
+ valueString: stringifySequencePropSourceEdit(edit.sourceEdit, parsedValue),
102
114
  defaultValue: parsedDefaultValue,
103
115
  defaultValueString: parsedDefaultValue !== null
104
116
  ? JSON.stringify(parsedDefaultValue)
@@ -179,6 +191,7 @@ const saveSequencePropsHandler = ({ input: { edits, addedKeyframes, movedKeyfram
179
191
  const snapshots = [];
180
192
  const outputByPath = new Map();
181
193
  const resultByIndex = new Map();
194
+ const updatedNodePaths = new Map();
182
195
  const sequenceKeyframeLogs = [];
183
196
  const captionPatchLogs = [];
184
197
  const effectKeyframeLogs = [];
@@ -204,7 +217,9 @@ const saveSequencePropsHandler = ({ input: { edits, addedKeyframes, movedKeyfram
204
217
  logLine: result.logLine,
205
218
  removedProps: result.removedProps,
206
219
  formatted,
220
+ newNodePath: result.newNodePath,
207
221
  });
222
+ updatedNodePaths.set(`${absolutePath}:${JSON.stringify(edit.nodePath.nodePath)}`, result.newNodePath);
208
223
  }
209
224
  }
210
225
  for (const captionPatchRequest of group.captionPatches) {
@@ -363,7 +378,10 @@ const saveSequencePropsHandler = ({ input: { edits, addedKeyframes, movedKeyfram
363
378
  const suppressHmr = captionPatches.length === 0 &&
364
379
  (0, exports.shouldSuppressHmrForSequencePropEdits)(edits);
365
380
  (0, undo_stack_1.pushTransactionToUndoStack)({
366
- snapshots,
381
+ snapshots: snapshots.map((snapshot) => ({
382
+ ...snapshot,
383
+ nodePathRemappings: null,
384
+ })),
367
385
  logLevel,
368
386
  remotionRoot,
369
387
  description: { undoMessage, redoMessage },
@@ -375,7 +393,12 @@ const saveSequencePropsHandler = ({ input: { edits, addedKeyframes, movedKeyfram
375
393
  if (suppressHmr) {
376
394
  (0, watch_ignore_next_change_1.suppressBundlerUpdateForFile)(absolutePath);
377
395
  }
378
- (0, file_watcher_1.writeFileAndNotifyFileWatchers)(absolutePath, output, clientId);
396
+ (0, file_watcher_1.writeFileAndNotifyFileWatchers)({
397
+ file: absolutePath,
398
+ content: output,
399
+ originatorClientId: clientId,
400
+ metadata: null,
401
+ });
379
402
  }
380
403
  for (const { edits: groupEdits, fileRelativeToRoot } of editGroups.values()) {
381
404
  for (const edit of groupEdits) {
@@ -448,6 +471,7 @@ const saveSequencePropsHandler = ({ input: { edits, addedKeyframes, movedKeyfram
448
471
  ])).values(),
449
472
  ];
450
473
  const results = statusTargets.map((target) => {
474
+ var _a;
451
475
  const { absolutePath } = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
452
476
  remotionRoot,
453
477
  fileName: target.fileName,
@@ -461,7 +485,7 @@ const saveSequencePropsHandler = ({ input: { edits, addedKeyframes, movedKeyfram
461
485
  fileContents: output,
462
486
  keys: (0, studio_shared_1.getAllSchemaKeys)(target.schema),
463
487
  assetKeys: (0, studio_shared_1.getAssetSchemaKeys)(target.schema),
464
- nodePath: target.nodePath.nodePath,
488
+ nodePath: (_a = updatedNodePaths.get(`${absolutePath}:${JSON.stringify(target.nodePath.nodePath)}`)) !== null && _a !== void 0 ? _a : target.nodePath.nodePath,
465
489
  componentIdentity: null,
466
490
  effects: [],
467
491
  videoConfigValues: target.nodePath.videoConfigValues,
@@ -7,6 +7,7 @@ const split_jsx_sequence_1 = require("../../codemods/split-jsx-sequence");
7
7
  const file_watcher_1 = require("../../file-watcher");
8
8
  const resolve_file_inside_project_1 = require("../../helpers/resolve-file-inside-project");
9
9
  const format_log_file_location_1 = require("../format-log-file-location");
10
+ const sequence_node_path_mutation_1 = require("../sequence-node-path-mutation");
10
11
  const undo_stack_1 = require("../undo-stack");
11
12
  const log_update_1 = require("./log-updates/log-update");
12
13
  const source_file_write_queue_1 = require("./source-file-write-queue");
@@ -19,11 +20,14 @@ const splitJsxSequenceHandler = ({ input: { fileName, nodePath, splitFrame }, re
19
20
  action: 'modify',
20
21
  });
21
22
  const fileContents = (0, node_fs_1.readFileSync)(absolutePath, 'utf-8');
22
- const { output, formatted, nodeLabel, logLine } = await (0, split_jsx_sequence_1.splitJsxSequence)({
23
- input: fileContents,
24
- nodePath,
25
- splitFrame,
26
- });
23
+ const { output, formatted, nodeLabel, logLine, nodePathRemappings } = await (0, split_jsx_sequence_1.splitJsxSequence)({ input: fileContents, nodePath, splitFrame });
24
+ const nodePathMutation = (0, sequence_node_path_mutation_1.broadcastSequenceNodePathMutation)([
25
+ {
26
+ absolutePath,
27
+ remappings: nodePathRemappings,
28
+ restoredNodePaths: [],
29
+ },
30
+ ]);
27
31
  (0, undo_stack_1.pushToUndoStack)({
28
32
  filePath: absolutePath,
29
33
  oldContents: fileContents,
@@ -37,9 +41,15 @@ const splitJsxSequenceHandler = ({ input: { fileName, nodePath, splitFrame }, re
37
41
  },
38
42
  entryType: 'split-jsx-sequence',
39
43
  suppressHmrOnFileRestore: false,
44
+ nodePathRemappings,
40
45
  });
41
46
  (0, undo_stack_1.suppressUndoStackInvalidation)(absolutePath);
42
- (0, file_watcher_1.writeFileAndNotifyFileWatchers)(absolutePath, output, undefined);
47
+ (0, file_watcher_1.writeFileAndNotifyFileWatchers)({
48
+ file: absolutePath,
49
+ content: output,
50
+ originatorClientId: undefined,
51
+ metadata: { skipSequencePropsUpdate: true },
52
+ });
43
53
  const locationLabel = (0, format_log_file_location_1.formatLogFileLocation)({
44
54
  remotionRoot,
45
55
  absolutePath,
@@ -53,6 +63,7 @@ const splitJsxSequenceHandler = ({ input: { fileName, nodePath, splitFrame }, re
53
63
  (0, undo_stack_1.printUndoHint)(logLevel);
54
64
  return {
55
65
  success: true,
66
+ nodePathMutation,
56
67
  };
57
68
  }
58
69
  catch (err) {
@@ -182,7 +182,12 @@ const updateConfigHandler = async ({ input, configFile }) => {
182
182
  }
183
183
  }
184
184
  const configContents = (0, node_fs_1.readFileSync)(configFile, 'utf8');
185
- (0, file_watcher_1.writeFileAndNotifyFileWatchers)(configFile, (0, exports.updateConfigFile)({ configContents, updates: input.updates }), input.clientId);
185
+ (0, file_watcher_1.writeFileAndNotifyFileWatchers)({
186
+ file: configFile,
187
+ content: (0, exports.updateConfigFile)({ configContents, updates: input.updates }),
188
+ originatorClientId: input.clientId,
189
+ metadata: null,
190
+ });
186
191
  return { success: true };
187
192
  };
188
193
  exports.updateConfigHandler = updateConfigHandler;
@@ -45,10 +45,16 @@ const updateDefaultPropsHandler = ({ input: { compositionId, defaultProps, enumP
45
45
  },
46
46
  entryType: 'default-props',
47
47
  suppressHmrOnFileRestore: true,
48
+ nodePathRemappings: null,
48
49
  });
49
50
  (0, undo_stack_1.suppressUndoStackInvalidation)(projectInfo.rootFile);
50
51
  (0, watch_ignore_next_change_1.suppressBundlerUpdateForFile)(projectInfo.rootFile);
51
- (0, file_watcher_1.writeFileAndNotifyFileWatchers)(projectInfo.rootFile, output, undefined);
52
+ (0, file_watcher_1.writeFileAndNotifyFileWatchers)({
53
+ file: projectInfo.rootFile,
54
+ content: output,
55
+ originatorClientId: undefined,
56
+ metadata: null,
57
+ });
52
58
  const locationLabel = (0, format_log_file_location_1.formatLogFileLocation)({
53
59
  remotionRoot,
54
60
  absolutePath: projectInfo.rootFile,
@@ -62,10 +62,16 @@ const updateEffectKeyframeSettingsHandler = ({ input: { fileName, sequenceNodePa
62
62
  },
63
63
  entryType: 'effect-props',
64
64
  suppressHmrOnFileRestore: true,
65
+ nodePathRemappings: null,
65
66
  });
66
67
  (0, undo_stack_1.suppressUndoStackInvalidation)(absolutePath);
67
68
  (0, watch_ignore_next_change_1.suppressBundlerUpdateForFile)(absolutePath);
68
- (0, file_watcher_1.writeFileAndNotifyFileWatchers)(absolutePath, output, clientId);
69
+ (0, file_watcher_1.writeFileAndNotifyFileWatchers)({
70
+ file: absolutePath,
71
+ content: output,
72
+ originatorClientId: clientId,
73
+ metadata: null,
74
+ });
69
75
  (0, log_effect_update_1.logEffectUpdate)({
70
76
  fileRelativeToRoot,
71
77
  line: logLine,
@@ -58,10 +58,16 @@ const updateSequenceKeyframeSettingsHandler = ({ input: { fileName, nodePath, ke
58
58
  },
59
59
  entryType: 'sequence-props',
60
60
  suppressHmrOnFileRestore: true,
61
+ nodePathRemappings: null,
61
62
  });
62
63
  (0, undo_stack_1.suppressUndoStackInvalidation)(absolutePath);
63
64
  (0, watch_ignore_next_change_1.suppressBundlerUpdateForFile)(absolutePath);
64
- (0, file_watcher_1.writeFileAndNotifyFileWatchers)(absolutePath, output, clientId);
65
+ (0, file_watcher_1.writeFileAndNotifyFileWatchers)({
66
+ file: absolutePath,
67
+ content: output,
68
+ originatorClientId: clientId,
69
+ metadata: null,
70
+ });
65
71
  (0, log_update_1.logUpdate)({
66
72
  fileRelativeToRoot,
67
73
  line: logLine,
@@ -0,0 +1,7 @@
1
+ import type { SequenceNodePathMutation, SequenceNodePathRemapping } from '@remotion/studio-shared';
2
+ import type { SequenceNodePath } from 'remotion';
3
+ export declare const broadcastSequenceNodePathMutation: (files: {
4
+ absolutePath: string;
5
+ remappings: SequenceNodePathRemapping[];
6
+ restoredNodePaths: SequenceNodePath[];
7
+ }[]) => SequenceNodePathMutation;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.broadcastSequenceNodePathMutation = void 0;
4
+ const node_crypto_1 = require("node:crypto");
5
+ const live_events_1 = require("./live-events");
6
+ const mutationSessionId = (0, node_crypto_1.randomUUID)();
7
+ let mutationCounter = 0;
8
+ const broadcastSequenceNodePathMutation = (files) => {
9
+ var _a;
10
+ mutationCounter++;
11
+ const mutation = {
12
+ mutationId: `${mutationSessionId}:${mutationCounter}`,
13
+ files,
14
+ };
15
+ (_a = (0, live_events_1.getLiveEventsListener)()) === null || _a === void 0 ? void 0 : _a.sendEventToClient({
16
+ type: 'sequence-node-paths-remapped',
17
+ mutation,
18
+ });
19
+ return mutation;
20
+ };
21
+ exports.broadcastSequenceNodePathMutation = broadcastSequenceNodePathMutation;
@@ -122,11 +122,16 @@ const subscribeToSequencePropsWatchers = ({ fileName, line, column, nodePath: pr
122
122
  (0, node_path_cache_1.setCachedNodePath)(fileName, line, column, initialResult.nodePath.nodePath);
123
123
  const { nodePath } = initialResult;
124
124
  const watcherKey = getWatcherKey(nodePath, assetKeys);
125
- // If a watcher already exists for this key, just bump the ref count
126
- if ((_a = sequencePropsWatchers[clientId]) === null || _a === void 0 ? void 0 : _a[watcherKey]) {
127
- sequencePropsWatchers[clientId][watcherKey].refCount++;
125
+ const existingWatcher = (_a = sequencePropsWatchers[clientId]) === null || _a === void 0 ? void 0 : _a[watcherKey];
126
+ if (existingWatcher) {
127
+ existingWatcher.refCount++;
128
128
  return initialResult;
129
129
  }
130
+ const watcherInfo = {
131
+ unwatch: () => undefined,
132
+ refCount: 1,
133
+ currentNodePath: nodePath,
134
+ };
130
135
  const { unwatch } = (0, file_watcher_1.installFileWatcher)({
131
136
  file: absolutePath,
132
137
  existenceOnly: false,
@@ -137,10 +142,13 @@ const subscribeToSequencePropsWatchers = ({ fileName, line, column, nodePath: pr
137
142
  if (event.originatorClientId === clientId) {
138
143
  return;
139
144
  }
145
+ if (event.type === 'changed' && event.skipSequencePropsUpdate) {
146
+ return;
147
+ }
140
148
  try {
141
149
  const result = (0, can_update_sequence_props_1.computeSequencePropsStatusFromContent)({
142
150
  fileContents: event.content,
143
- nodePath: nodePath.nodePath,
151
+ nodePath: watcherInfo.currentNodePath.nodePath,
144
152
  componentIdentity,
145
153
  keys,
146
154
  assetKeys,
@@ -158,7 +166,7 @@ const subscribeToSequencePropsWatchers = ({ fileName, line, column, nodePath: pr
158
166
  listener.sendEventToClientId(clientId, {
159
167
  type: 'sequence-props-updated',
160
168
  fileName,
161
- nodePath,
169
+ nodePath: watcherInfo.currentNodePath,
162
170
  result,
163
171
  });
164
172
  });
@@ -180,10 +188,11 @@ const subscribeToSequencePropsWatchers = ({ fileName, line, column, nodePath: pr
180
188
  }
181
189
  },
182
190
  });
191
+ watcherInfo.unwatch = unwatch;
183
192
  if (!sequencePropsWatchers[clientId]) {
184
193
  sequencePropsWatchers[clientId] = {};
185
194
  }
186
- sequencePropsWatchers[clientId][watcherKey] = { unwatch, refCount: 1 };
195
+ sequencePropsWatchers[clientId][watcherKey] = watcherInfo;
187
196
  return initialResult;
188
197
  };
189
198
  exports.subscribeToSequencePropsWatchers = subscribeToSequencePropsWatchers;
@@ -37,6 +37,7 @@ export declare const startServer: (options: {
37
37
  binariesDirectory: string | null;
38
38
  forceIPv4: boolean;
39
39
  getAudioLatencyHint: () => AudioContextLatencyCategory | null;
40
+ getExperimentalKeepAudioContextAlive: () => boolean;
40
41
  getPreviewSampleRate: () => number | null;
41
42
  enableCrossSiteIsolation: boolean;
42
43
  forceNew: boolean;
@@ -99,6 +99,7 @@ const startServer = async (options) => {
99
99
  gitSource: options.gitSource,
100
100
  binariesDirectory: options.binariesDirectory,
101
101
  getAudioLatencyHint: options.getAudioLatencyHint,
102
+ getExperimentalKeepAudioContextAlive: options.getExperimentalKeepAudioContextAlive,
102
103
  getPreviewSampleRate: options.getPreviewSampleRate,
103
104
  enableCrossSiteIsolation: options.enableCrossSiteIsolation,
104
105
  getStudioRuntimeConfig: options.getStudioRuntimeConfig,
@@ -1,4 +1,5 @@
1
1
  import type { LogLevel } from '@remotion/renderer';
2
+ import type { RedoResponse, SequenceNodePathRemapping, UndoResponse } from '@remotion/studio-shared';
2
3
  export interface UndoEntryDescription {
3
4
  undoMessage: string;
4
5
  redoMessage: string;
@@ -10,6 +11,7 @@ type UndoEntrySnapshot = {
10
11
  newContents: string | null;
11
12
  /** 1-based source line for terminal/IDE file links (e.g. path:line). */
12
13
  logLine: number;
14
+ nodePathRemappings: SequenceNodePathRemapping[] | null;
13
15
  };
14
16
  type UndoEntry = {
15
17
  filePath: string;
@@ -70,7 +72,7 @@ type UndoEntry = {
70
72
  } | {
71
73
  entryType: 'rename-folder';
72
74
  });
73
- export declare function pushToUndoStack({ filePath, oldContents, newContents, logLevel, remotionRoot, logLine, description, entryType, suppressHmrOnFileRestore }: {
75
+ export declare function pushToUndoStack({ filePath, oldContents, newContents, logLevel, remotionRoot, logLine, description, entryType, suppressHmrOnFileRestore, nodePathRemappings }: {
74
76
  filePath: string;
75
77
  oldContents: string;
76
78
  newContents: string | null;
@@ -80,6 +82,7 @@ export declare function pushToUndoStack({ filePath, oldContents, newContents, lo
80
82
  description: UndoEntryDescription;
81
83
  entryType: UndoEntryType;
82
84
  suppressHmrOnFileRestore: boolean;
85
+ nodePathRemappings: SequenceNodePathRemapping[] | null;
83
86
  }): void;
84
87
  export declare function pushTransactionToUndoStack({ snapshots, logLevel, remotionRoot, description, entryType, suppressHmrOnFileRestore }: {
85
88
  snapshots: Array<{
@@ -87,6 +90,7 @@ export declare function pushTransactionToUndoStack({ snapshots, logLevel, remoti
87
90
  oldContents: string | null;
88
91
  newContents: string | null;
89
92
  logLine: number;
93
+ nodePathRemappings: SequenceNodePathRemapping[] | null;
90
94
  }>;
91
95
  logLevel: LogLevel;
92
96
  remotionRoot: string;
@@ -105,18 +109,8 @@ export declare function pushToRedoStack({ filePath, oldContents, newContents, lo
105
109
  suppressHmrOnFileRestore: boolean;
106
110
  }): void;
107
111
  export declare function suppressUndoStackInvalidation(filePath: string): void;
108
- export declare function popUndo(): {
109
- success: true;
110
- } | {
111
- success: false;
112
- reason: string;
113
- };
114
- export declare function popRedo(): {
115
- success: true;
116
- } | {
117
- success: false;
118
- reason: string;
119
- };
112
+ export declare function popUndo(): UndoResponse;
113
+ export declare function popRedo(): RedoResponse;
120
114
  export declare function getUndoStack(): readonly UndoEntry[];
121
115
  export declare function getRedoStack(): readonly UndoEntry[];
122
116
  export declare function clearUndoStackForTests(): void;
@@ -17,6 +17,7 @@ const read_visual_control_values_1 = require("../codemods/read-visual-control-va
17
17
  const file_watcher_1 = require("../file-watcher");
18
18
  const format_log_file_location_1 = require("./format-log-file-location");
19
19
  const live_events_1 = require("./live-events");
20
+ const sequence_node_path_mutation_1 = require("./sequence-node-path-mutation");
20
21
  const watch_ignore_next_change_1 = require("./watch-ignore-next-change");
21
22
  const MAX_ENTRIES = 100;
22
23
  const undoStack = [];
@@ -56,7 +57,7 @@ const makeUndoEntry = ({ snapshots, description, entryType, suppressHmrOnFileRes
56
57
  suppressHmrOnFileRestore,
57
58
  };
58
59
  };
59
- function pushToUndoStack({ filePath, oldContents, newContents, logLevel, remotionRoot, logLine, description, entryType, suppressHmrOnFileRestore, }) {
60
+ function pushToUndoStack({ filePath, oldContents, newContents, logLevel, remotionRoot, logLine, description, entryType, suppressHmrOnFileRestore, nodePathRemappings, }) {
60
61
  pushTransactionToUndoStack({
61
62
  snapshots: [
62
63
  {
@@ -64,6 +65,7 @@ function pushToUndoStack({ filePath, oldContents, newContents, logLevel, remotio
64
65
  oldContents,
65
66
  newContents,
66
67
  logLine,
68
+ nodePathRemappings,
67
69
  },
68
70
  ],
69
71
  logLevel,
@@ -108,6 +110,7 @@ function pushToRedoStack({ filePath, oldContents, newContents, logLine, descript
108
110
  oldContents,
109
111
  newContents,
110
112
  logLine,
113
+ nodePathRemappings: null,
111
114
  },
112
115
  ],
113
116
  description,
@@ -247,6 +250,29 @@ function popUndo() {
247
250
  if (redoStack.length > MAX_ENTRIES) {
248
251
  redoStack.shift();
249
252
  }
253
+ const files = entry.snapshots.flatMap((snapshot) => {
254
+ if (snapshot.nodePathRemappings === null) {
255
+ return [];
256
+ }
257
+ return [
258
+ {
259
+ absolutePath: snapshot.filePath,
260
+ remappings: snapshot.nodePathRemappings.flatMap((remapping) => {
261
+ if (remapping.newNodePath === null) {
262
+ return [];
263
+ }
264
+ return [
265
+ {
266
+ oldNodePath: remapping.newNodePath,
267
+ newNodePath: remapping.oldNodePath,
268
+ },
269
+ ];
270
+ }),
271
+ restoredNodePaths: snapshot.nodePathRemappings.flatMap((remapping) => remapping.newNodePath === null ? [remapping.oldNodePath] : []),
272
+ },
273
+ ];
274
+ });
275
+ const nodePathMutation = files.length === 0 ? null : (0, sequence_node_path_mutation_1.broadcastSequenceNodePathMutation)(files);
250
276
  for (const snapshot of entry.snapshots) {
251
277
  suppressUndoStackInvalidation(snapshot.filePath);
252
278
  if (entry.suppressHmrOnFileRestore) {
@@ -256,7 +282,14 @@ function popUndo() {
256
282
  (0, node_fs_1.rmSync)(snapshot.filePath, { force: true });
257
283
  }
258
284
  else {
259
- (0, file_watcher_1.writeFileAndNotifyFileWatchers)(snapshot.filePath, snapshot.oldContents, undefined);
285
+ (0, file_watcher_1.writeFileAndNotifyFileWatchers)({
286
+ file: snapshot.filePath,
287
+ content: snapshot.oldContents,
288
+ originatorClientId: undefined,
289
+ metadata: snapshot.nodePathRemappings === null
290
+ ? null
291
+ : { skipSequencePropsUpdate: true },
292
+ });
260
293
  }
261
294
  }
262
295
  renderer_1.RenderInternals.Log.verbose({ indent: false, logLevel: storedLogLevel }, renderer_1.RenderInternals.chalk.gray(`Undo: restored ${entry.filePath} (undo: ${undoStack.length}, redo: ${redoStack.length})`));
@@ -272,7 +305,7 @@ function popUndo() {
272
305
  ensureWatching(filePath);
273
306
  }
274
307
  broadcastState();
275
- return { success: true };
308
+ return { success: true, nodePathMutation };
276
309
  }
277
310
  function popRedo() {
278
311
  const entry = redoStack.pop();
@@ -298,12 +331,32 @@ function popRedo() {
298
331
  if (undoStack.length > MAX_ENTRIES) {
299
332
  undoStack.shift();
300
333
  }
334
+ const files = snapshotsWithNewContents.flatMap((snapshot) => {
335
+ if (snapshot.nodePathRemappings === null) {
336
+ return [];
337
+ }
338
+ return [
339
+ {
340
+ absolutePath: snapshot.filePath,
341
+ remappings: snapshot.nodePathRemappings,
342
+ restoredNodePaths: [],
343
+ },
344
+ ];
345
+ });
346
+ const nodePathMutation = files.length === 0 ? null : (0, sequence_node_path_mutation_1.broadcastSequenceNodePathMutation)(files);
301
347
  for (const snapshot of snapshotsWithNewContents) {
302
348
  suppressUndoStackInvalidation(snapshot.filePath);
303
349
  if (entry.suppressHmrOnFileRestore) {
304
350
  (0, watch_ignore_next_change_1.suppressBundlerUpdateForFile)(snapshot.filePath);
305
351
  }
306
- (0, file_watcher_1.writeFileAndNotifyFileWatchers)(snapshot.filePath, snapshot.newContents, undefined);
352
+ (0, file_watcher_1.writeFileAndNotifyFileWatchers)({
353
+ file: snapshot.filePath,
354
+ content: snapshot.newContents,
355
+ originatorClientId: undefined,
356
+ metadata: snapshot.nodePathRemappings === null
357
+ ? null
358
+ : { skipSequencePropsUpdate: true },
359
+ });
307
360
  }
308
361
  renderer_1.RenderInternals.Log.verbose({ indent: false, logLevel: storedLogLevel }, renderer_1.RenderInternals.chalk.gray(`Redo: restored ${entry.filePath} (undo: ${undoStack.length}, redo: ${redoStack.length})`));
309
362
  logFileAction(entry.description.redoMessage, entry.filePath, entry.logLine);
@@ -318,7 +371,7 @@ function popRedo() {
318
371
  ensureWatching(filePath);
319
372
  }
320
373
  broadcastState();
321
- return { success: true };
374
+ return { success: true, nodePathMutation };
322
375
  }
323
376
  /*
324
377
  * Keep stack accessors typed as readonly arrays so callers can only inspect
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, 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, }: {
7
7
  staticHash: string;
8
8
  staticHashPrefix: string;
9
9
  outputHash: string;
@@ -24,6 +24,7 @@ export declare const handleRoutes: ({ staticHash, staticHashPrefix, outputHash,
24
24
  gitSource: GitSource | null;
25
25
  binariesDirectory: string | null;
26
26
  getAudioLatencyHint: () => AudioContextLatencyCategory | null;
27
+ getExperimentalKeepAudioContextAlive: () => boolean;
27
28
  getPreviewSampleRate: () => number | null;
28
29
  enableCrossSiteIsolation: boolean;
29
30
  getStudioRuntimeConfig: () => StudioRuntimeConfig;
package/dist/routes.js CHANGED
@@ -51,6 +51,7 @@ const get_package_manager_1 = require("./preview-server/get-package-manager");
51
51
  const get_static_file_fallback_hint_1 = require("./preview-server/get-static-file-fallback-hint");
52
52
  const handler_1 = require("./preview-server/handler");
53
53
  const public_folder_1 = require("./preview-server/public-folder");
54
+ const app_icon_1 = require("./preview-server/routes/app-icon");
54
55
  const open_in_editor_1 = require("./preview-server/routes/open-in-editor");
55
56
  const serve_static_1 = require("./preview-server/serve-static");
56
57
  const handle_discovery_1 = require("./preview-server/studio-protocol/handle-discovery");
@@ -83,7 +84,7 @@ const handleRemotionConfig = (response, remotionRoot) => {
83
84
  response.end(JSON.stringify(body));
84
85
  return Promise.resolve();
85
86
  };
86
- const handleFallback = async ({ remotionRoot, hash, response, request, getCurrentInputProps, getEnvVariables, publicDir, getRenderQueue, getRenderDefaults, getNumberOfAudioTags, getAudioLatencyHint, getPreviewSampleRate, gitSource, logLevel, enableCrossSiteIsolation, getStudioRuntimeConfig, getDefaultEditor, }) => {
87
+ const handleFallback = async ({ remotionRoot, hash, response, request, getCurrentInputProps, getEnvVariables, publicDir, getRenderQueue, getRenderDefaults, getNumberOfAudioTags, getAudioLatencyHint, getExperimentalKeepAudioContextAlive, getPreviewSampleRate, gitSource, logLevel, enableCrossSiteIsolation, getStudioRuntimeConfig, getDefaultEditor, }) => {
87
88
  var _a, _b;
88
89
  const acceptsHtml = ((_a = request.headers.accept) !== null && _a !== void 0 ? _a : '').includes('text/html');
89
90
  if (request.method === 'GET' && acceptsHtml) {
@@ -148,6 +149,7 @@ const handleFallback = async ({ remotionRoot, hash, response, request, getCurren
148
149
  logLevel,
149
150
  mode: 'dev',
150
151
  audioLatencyHint: (_b = getAudioLatencyHint()) !== null && _b !== void 0 ? _b : 'playback',
152
+ experimentalKeepAudioContextAlive: getExperimentalKeepAudioContextAlive(),
151
153
  sampleRate: getPreviewSampleRate(),
152
154
  studioRuntimeConfig: getStudioRuntimeConfig(),
153
155
  }));
@@ -252,7 +254,7 @@ const handleBeep = (_, response) => {
252
254
  readStream.pipe(response);
253
255
  return Promise.resolve();
254
256
  };
255
- const handleRoutes = ({ staticHash, staticHashPrefix, outputHash, outputHashPrefix, request, response, liveEventsServer, getCurrentInputProps, getEnvVariables, remotionRoot, entryPoint, publicDir, logLevel, getRenderQueue, getRenderDefaults, getNumberOfAudioTags, queueMethods: methods, gitSource, binariesDirectory, getAudioLatencyHint, getPreviewSampleRate, enableCrossSiteIsolation, getStudioRuntimeConfig, getDefaultCodingAgent, getDefaultEditor, configFile, }) => {
257
+ 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, }) => {
256
258
  const url = new URL(request.url, 'http://localhost');
257
259
  if (url.pathname === '/api/file-source') {
258
260
  return handleFileSource({
@@ -279,6 +281,13 @@ const handleRoutes = ({ staticHash, staticHashPrefix, outputHash, outputHashPref
279
281
  remotionRoot,
280
282
  });
281
283
  }
284
+ if (url.pathname.startsWith('/api/app-icon/')) {
285
+ return (0, app_icon_1.handleAppIcon)({
286
+ pathname: url.pathname,
287
+ request,
288
+ response,
289
+ });
290
+ }
282
291
  if (url.pathname === '/api/studio-protocol' ||
283
292
  url.pathname === '/api/studio-protocol/install' ||
284
293
  url.pathname === '/api/studio-protocol/license-key') {
@@ -389,6 +398,7 @@ const handleRoutes = ({ staticHash, staticHashPrefix, outputHash, outputHashPref
389
398
  gitSource,
390
399
  logLevel,
391
400
  getAudioLatencyHint,
401
+ getExperimentalKeepAudioContextAlive,
392
402
  getPreviewSampleRate,
393
403
  enableCrossSiteIsolation,
394
404
  getStudioRuntimeConfig,