@remotion/studio 4.0.469 → 4.0.471

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 (101) hide show
  1. package/dist/Studio.js +1 -1
  2. package/dist/api/save-render-output.js +3 -12
  3. package/dist/components/AudioWaveform.js +19 -2
  4. package/dist/components/ContextMenu.d.ts +7 -2
  5. package/dist/components/ContextMenu.js +53 -9
  6. package/dist/components/EditorContent.js +5 -4
  7. package/dist/components/Menu/MenuItem.d.ts +1 -1
  8. package/dist/components/MenuBuildIndicator.js +0 -1
  9. package/dist/components/NewComposition/InputDragger.js +1 -0
  10. package/dist/components/Preview.js +6 -2
  11. package/dist/components/SelectedOutlineOverlay.d.ts +49 -0
  12. package/dist/components/SelectedOutlineOverlay.js +710 -0
  13. package/dist/components/Timeline/Timeline.js +102 -13
  14. package/dist/components/Timeline/TimelineDeleteKeybindings.d.ts +2 -0
  15. package/dist/components/Timeline/TimelineDeleteKeybindings.js +101 -0
  16. package/dist/components/Timeline/TimelineDragHandler.js +20 -244
  17. package/dist/components/Timeline/{TimelineEffectGroupRow.d.ts → TimelineEffectItem.d.ts} +1 -1
  18. package/dist/components/Timeline/TimelineEffectItem.js +323 -0
  19. package/dist/components/Timeline/{TimelineEffectFieldRow.d.ts → TimelineEffectPropItem.d.ts} +2 -1
  20. package/dist/components/Timeline/{TimelineEffectFieldRow.js → TimelineEffectPropItem.js} +97 -8
  21. package/dist/components/Timeline/TimelineExpandArrowButton.js +5 -1
  22. package/dist/components/Timeline/TimelineExpandedKeyframeRow.js +37 -5
  23. package/dist/components/Timeline/TimelineExpandedRow.d.ts +1 -0
  24. package/dist/components/Timeline/TimelineExpandedRow.js +9 -7
  25. package/dist/components/Timeline/TimelineExpandedSection.d.ts +1 -0
  26. package/dist/components/Timeline/TimelineExpandedSection.js +2 -2
  27. package/dist/components/Timeline/TimelineInOutDragHandler.d.ts +2 -0
  28. package/dist/components/Timeline/TimelineInOutDragHandler.js +324 -0
  29. package/dist/components/Timeline/TimelineInOutPointer.js +3 -1
  30. package/dist/components/Timeline/TimelineInOutPointerHandle.d.ts +1 -0
  31. package/dist/components/Timeline/TimelineInOutPointerHandle.js +5 -4
  32. package/dist/components/Timeline/TimelineKeyframeControls.d.ts +17 -0
  33. package/dist/components/Timeline/TimelineKeyframeControls.js +222 -0
  34. package/dist/components/Timeline/TimelineKeyframeDiamond.js +7 -6
  35. package/dist/components/Timeline/TimelineKeyframedValue.d.ts +8 -0
  36. package/dist/components/Timeline/TimelineKeyframedValue.js +36 -0
  37. package/dist/components/Timeline/TimelineList.js +3 -15
  38. package/dist/components/Timeline/TimelineRowChrome.d.ts +6 -1
  39. package/dist/components/Timeline/TimelineRowChrome.js +25 -7
  40. package/dist/components/Timeline/TimelineSelection.d.ts +53 -9
  41. package/dist/components/Timeline/TimelineSelection.js +305 -48
  42. package/dist/components/Timeline/TimelineSequence.d.ts +2 -0
  43. package/dist/components/Timeline/TimelineSequence.js +18 -6
  44. package/dist/components/Timeline/TimelineSequenceFrame.js +1 -0
  45. package/dist/components/Timeline/{TimelineListItem.d.ts → TimelineSequenceItem.d.ts} +2 -1
  46. package/dist/components/Timeline/{TimelineListItem.js → TimelineSequenceItem.js} +140 -34
  47. package/dist/components/Timeline/{TimelineFieldRow.d.ts → TimelineSequencePropItem.d.ts} +2 -1
  48. package/dist/components/Timeline/{TimelineFieldRow.js → TimelineSequencePropItem.js} +81 -5
  49. package/dist/components/Timeline/TimelineTimeIndicators.js +0 -1
  50. package/dist/components/Timeline/TimelineTrack.js +3 -1
  51. package/dist/components/Timeline/TimelineTranslateField.js +14 -22
  52. package/dist/components/Timeline/call-add-keyframe.d.ts +23 -0
  53. package/dist/components/Timeline/call-add-keyframe.js +54 -0
  54. package/dist/components/Timeline/call-delete-keyframe.d.ts +37 -0
  55. package/dist/components/Timeline/call-delete-keyframe.js +122 -0
  56. package/dist/components/Timeline/delete-selected-keyframe.d.ts +21 -0
  57. package/dist/components/Timeline/delete-selected-keyframe.js +92 -0
  58. package/dist/components/Timeline/delete-selected-timeline-item.d.ts +17 -0
  59. package/dist/components/Timeline/delete-selected-timeline-item.js +178 -0
  60. package/dist/components/Timeline/duplicate-selected-timeline-item.d.ts +13 -0
  61. package/dist/components/Timeline/duplicate-selected-timeline-item.js +66 -0
  62. package/dist/components/Timeline/find-track-for-node-path-info.d.ts +7 -0
  63. package/dist/components/Timeline/find-track-for-node-path-info.js +13 -0
  64. package/dist/components/Timeline/get-keyframe-navigation.d.ts +9 -0
  65. package/dist/components/Timeline/get-keyframe-navigation.js +26 -0
  66. package/dist/components/Timeline/parse-keyframe-field-from-node-path.d.ts +8 -0
  67. package/dist/components/Timeline/parse-keyframe-field-from-node-path.js +26 -0
  68. package/dist/components/Timeline/reset-selected-timeline-props.d.ts +38 -0
  69. package/dist/components/Timeline/reset-selected-timeline-props.js +143 -0
  70. package/dist/components/Timeline/save-sequence-prop.d.ts +14 -2
  71. package/dist/components/Timeline/save-sequence-prop.js +42 -7
  72. package/dist/components/Timeline/sequence-props-subscription-store.d.ts +3 -2
  73. package/dist/components/Timeline/sequence-props-subscription-store.js +2 -1
  74. package/dist/components/Timeline/timeline-row-layout.d.ts +1 -0
  75. package/dist/components/Timeline/timeline-row-layout.js +2 -1
  76. package/dist/components/Timeline/timeline-scroll-logic.js +3 -3
  77. package/dist/components/Timeline/timeline-translate-utils.d.ts +2 -0
  78. package/dist/components/Timeline/timeline-translate-utils.js +20 -0
  79. package/dist/components/Timeline/use-expanded-track-keyframe-rows.js +10 -3
  80. package/dist/components/Timeline/use-sequence-props-subscription.js +2 -1
  81. package/dist/components/composition-menu-items.js +32 -1
  82. package/dist/error-overlay/remotion-overlay/OpenInEditor.js +0 -1
  83. package/dist/esm/{chunk-1mp51e0w.js → chunk-z0z9d4r0.js} +10422 -5958
  84. package/dist/esm/internals.mjs +10422 -5958
  85. package/dist/esm/previewEntry.mjs +10419 -5953
  86. package/dist/esm/renderEntry.mjs +3 -1
  87. package/dist/helpers/format-file-location.d.ts +9 -0
  88. package/dist/helpers/format-file-location.js +27 -0
  89. package/dist/helpers/get-box-quads-polyfill-internals.d.ts +82 -0
  90. package/dist/helpers/get-box-quads-polyfill-internals.js +2395 -0
  91. package/dist/helpers/get-box-quads-ponyfill.d.ts +10 -0
  92. package/dist/helpers/get-box-quads-ponyfill.js +23 -0
  93. package/dist/helpers/get-left-of-timeline-slider.js +1 -1
  94. package/dist/helpers/get-timeline-sequence-layout.js +10 -11
  95. package/dist/helpers/open-in-editor.d.ts +20 -2
  96. package/dist/helpers/open-in-editor.js +53 -30
  97. package/dist/helpers/use-menu-structure.js +8 -17
  98. package/dist/renderEntry.js +2 -2
  99. package/dist/state/z-index.js +5 -2
  100. package/package.json +10 -10
  101. package/dist/components/Timeline/TimelineEffectGroupRow.js +0 -153
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resetSelectedTimelineProps = exports.getTimelinePropResetTargets = void 0;
4
+ const remotion_1 = require("remotion");
5
+ const find_track_for_node_path_info_1 = require("./find-track-for-node-path-info");
6
+ const save_effect_prop_1 = require("./save-effect-prop");
7
+ const save_sequence_prop_1 = require("./save-sequence-prop");
8
+ const isPropResetSelection = (selection) => selection.type === 'sequence-prop' ||
9
+ selection.type === 'sequence-effect-prop';
10
+ const isVisibleFieldSchema = (fieldSchema) => fieldSchema !== undefined && fieldSchema.type !== 'hidden';
11
+ const isNonDefaultCodeValue = ({ codeValue, defaultValue, }) => JSON.stringify(codeValue !== null && codeValue !== void 0 ? codeValue : defaultValue) !== JSON.stringify(defaultValue);
12
+ const getDefaultValue = (fieldSchema) => fieldSchema.default !== undefined
13
+ ? JSON.stringify(fieldSchema.default)
14
+ : null;
15
+ const getTimelinePropResetTargets = ({ selections, sequences, overrideIdsToNodePaths, codeValues, }) => {
16
+ var _a;
17
+ var _b;
18
+ const firstSelection = selections[0];
19
+ if (!firstSelection || !isPropResetSelection(firstSelection)) {
20
+ return null;
21
+ }
22
+ const resetTargets = [];
23
+ for (const selection of selections) {
24
+ if (!isPropResetSelection(selection)) {
25
+ throw new Error(`Assertion failed: Cannot reset timeline selections of different types (${firstSelection.type}, ${selection.type})`);
26
+ }
27
+ const track = (0, find_track_for_node_path_info_1.findTrackForNodePathInfo)({
28
+ sequences,
29
+ overrideIdsToNodePaths,
30
+ nodePathInfo: selection.nodePathInfo,
31
+ });
32
+ const sequence = (_b = track === null || track === void 0 ? void 0 : track.sequence) !== null && _b !== void 0 ? _b : null;
33
+ if (!sequence) {
34
+ continue;
35
+ }
36
+ const nodePath = selection.nodePathInfo.sequenceSubscriptionKey;
37
+ if (selection.type === 'sequence-prop') {
38
+ if (!sequence.controls) {
39
+ continue;
40
+ }
41
+ const sequenceFieldSchema = sequence.controls.schema[selection.key];
42
+ const sequencePropStatus = (_a = remotion_1.Internals.getCodeValuesCtx(codeValues, nodePath)) === null || _a === void 0 ? void 0 : _a[selection.key];
43
+ if (!isVisibleFieldSchema(sequenceFieldSchema) ||
44
+ !(sequencePropStatus === null || sequencePropStatus === void 0 ? void 0 : sequencePropStatus.canUpdate) ||
45
+ !isNonDefaultCodeValue({
46
+ codeValue: sequencePropStatus.codeValue,
47
+ defaultValue: sequenceFieldSchema.default,
48
+ })) {
49
+ continue;
50
+ }
51
+ resetTargets.push({
52
+ type: 'sequence-prop',
53
+ fileName: nodePath.absolutePath,
54
+ nodePath,
55
+ fieldKey: selection.key,
56
+ value: sequenceFieldSchema.default,
57
+ defaultValue: getDefaultValue(sequenceFieldSchema),
58
+ schema: sequence.controls.schema,
59
+ });
60
+ continue;
61
+ }
62
+ const effect = sequence.effects[selection.i];
63
+ const fieldSchema = effect === null || effect === void 0 ? void 0 : effect.schema[selection.key];
64
+ const effectStatus = remotion_1.Internals.getEffectCodeValuesCtx({
65
+ codeValues,
66
+ nodePath,
67
+ effectIndex: selection.i,
68
+ });
69
+ const propStatus = effectStatus.type === 'can-update-effect'
70
+ ? effectStatus.props[selection.key]
71
+ : null;
72
+ if (!effect ||
73
+ !isVisibleFieldSchema(fieldSchema) ||
74
+ !(propStatus === null || propStatus === void 0 ? void 0 : propStatus.canUpdate) ||
75
+ !isNonDefaultCodeValue({
76
+ codeValue: propStatus.codeValue,
77
+ defaultValue: fieldSchema.default,
78
+ })) {
79
+ continue;
80
+ }
81
+ resetTargets.push({
82
+ type: 'effect-prop',
83
+ fileName: nodePath.absolutePath,
84
+ nodePath,
85
+ effectIndex: selection.i,
86
+ fieldKey: selection.key,
87
+ value: fieldSchema.default,
88
+ defaultValue: getDefaultValue(fieldSchema),
89
+ schema: effect.schema,
90
+ });
91
+ }
92
+ return resetTargets;
93
+ };
94
+ exports.getTimelinePropResetTargets = getTimelinePropResetTargets;
95
+ const resetSelectedTimelineProps = ({ selections, sequences, overrideIdsToNodePaths, codeValues, setCodeValues, clientId, }) => {
96
+ const resetTargets = (0, exports.getTimelinePropResetTargets)({
97
+ selections,
98
+ sequences,
99
+ overrideIdsToNodePaths,
100
+ codeValues,
101
+ });
102
+ if (resetTargets === null || resetTargets.length === 0) {
103
+ return null;
104
+ }
105
+ const sequencePropTargets = resetTargets.filter((target) => target.type === 'sequence-prop');
106
+ const effectPropTargets = resetTargets.filter((target) => target.type === 'effect-prop');
107
+ const resetPromises = [];
108
+ if (sequencePropTargets.length > 0) {
109
+ resetPromises.push((0, save_sequence_prop_1.saveSequenceProps)({
110
+ changes: sequencePropTargets.map((target) => ({
111
+ fileName: target.fileName,
112
+ nodePath: target.nodePath,
113
+ fieldKey: target.fieldKey,
114
+ value: target.value,
115
+ defaultValue: target.defaultValue,
116
+ schema: target.schema,
117
+ })),
118
+ setCodeValues,
119
+ clientId,
120
+ undoLabel: sequencePropTargets.length > 1
121
+ ? 'Reset selected sequence props'
122
+ : null,
123
+ redoLabel: sequencePropTargets.length > 1
124
+ ? 'Reapply selected sequence props'
125
+ : null,
126
+ }));
127
+ }
128
+ for (const target of effectPropTargets) {
129
+ resetPromises.push((0, save_effect_prop_1.saveEffectProp)({
130
+ fileName: target.fileName,
131
+ nodePath: target.nodePath,
132
+ effectIndex: target.effectIndex,
133
+ fieldKey: target.fieldKey,
134
+ value: target.value,
135
+ defaultValue: target.defaultValue,
136
+ schema: target.schema,
137
+ setCodeValues,
138
+ clientId,
139
+ }));
140
+ }
141
+ return Promise.all(resetPromises).then(() => undefined);
142
+ };
143
+ exports.resetSelectedTimelineProps = resetSelectedTimelineProps;
@@ -1,12 +1,24 @@
1
1
  import type { CanUpdateSequencePropsResponse, SequencePropsSubscriptionKey, SequenceSchema } from 'remotion';
2
2
  export type SetCodeValues = (nodePath: SequencePropsSubscriptionKey, values: (prev: CanUpdateSequencePropsResponse) => CanUpdateSequencePropsResponse) => void;
3
- export declare const saveSequenceProp: ({ fileName, nodePath, fieldKey, value, defaultValue, schema, setCodeValues, clientId, }: {
3
+ export type SaveSequencePropChange = {
4
4
  fileName: string;
5
5
  nodePath: SequencePropsSubscriptionKey;
6
6
  fieldKey: string;
7
7
  value: unknown;
8
8
  defaultValue: string | null;
9
9
  schema: SequenceSchema;
10
+ };
11
+ type SaveSequencePropsOptions = {
12
+ changes: SaveSequencePropChange[];
10
13
  setCodeValues: SetCodeValues;
11
14
  clientId: string;
12
- }) => Promise<void>;
15
+ undoLabel: string | null;
16
+ redoLabel: string | null;
17
+ };
18
+ type SaveSequencePropOptions = SaveSequencePropChange & {
19
+ setCodeValues: SetCodeValues;
20
+ clientId: string;
21
+ };
22
+ export declare const saveSequenceProps: ({ changes, setCodeValues, clientId, undoLabel, redoLabel, }: SaveSequencePropsOptions) => Promise<void>;
23
+ export declare const saveSequenceProp: ({ fileName, nodePath, fieldKey, value, defaultValue, schema, setCodeValues, clientId, }: SaveSequencePropOptions) => Promise<void>;
24
+ export {};
@@ -1,9 +1,38 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.saveSequenceProp = void 0;
3
+ exports.saveSequenceProp = exports.saveSequenceProps = void 0;
4
4
  const studio_shared_1 = require("@remotion/studio-shared");
5
5
  const call_api_1 = require("../call-api");
6
6
  const save_prop_queue_1 = require("./save-prop-queue");
7
+ const saveSequenceProps = ({ changes, setCodeValues, clientId, undoLabel, redoLabel, }) => {
8
+ if (changes.length === 0) {
9
+ return Promise.resolve();
10
+ }
11
+ for (const change of changes) {
12
+ setCodeValues(change.nodePath, (prev) => (0, studio_shared_1.optimisticUpdateForCodeValues)({
13
+ previous: prev,
14
+ fieldKey: change.fieldKey,
15
+ value: change.value,
16
+ schema: change.schema,
17
+ }));
18
+ }
19
+ return (0, call_api_1.callApi)('/api/save-sequence-props', {
20
+ edits: changes.map((change) => {
21
+ return {
22
+ fileName: change.fileName,
23
+ nodePath: change.nodePath,
24
+ key: change.fieldKey,
25
+ value: JSON.stringify(change.value),
26
+ defaultValue: change.defaultValue,
27
+ schema: change.schema,
28
+ };
29
+ }),
30
+ clientId,
31
+ undoLabel,
32
+ redoLabel,
33
+ }).then(() => undefined);
34
+ };
35
+ exports.saveSequenceProps = saveSequenceProps;
7
36
  const saveSequenceProp = ({ fileName, nodePath, fieldKey, value, defaultValue, schema, setCodeValues, clientId, }) => {
8
37
  return (0, save_prop_queue_1.enqueueSavePropChange)({
9
38
  nodePath,
@@ -15,13 +44,19 @@ const saveSequenceProp = ({ fileName, nodePath, fieldKey, value, defaultValue, s
15
44
  schema,
16
45
  }),
17
46
  apiCall: () => (0, call_api_1.callApi)('/api/save-sequence-props', {
18
- fileName,
19
- nodePath,
20
- key: fieldKey,
21
- value: JSON.stringify(value),
22
- defaultValue,
23
- schema,
47
+ edits: [
48
+ {
49
+ fileName,
50
+ nodePath,
51
+ key: fieldKey,
52
+ value: JSON.stringify(value),
53
+ defaultValue,
54
+ schema,
55
+ },
56
+ ],
24
57
  clientId,
58
+ undoLabel: null,
59
+ redoLabel: null,
25
60
  }),
26
61
  errorLabel: 'Could not save sequence prop',
27
62
  });
@@ -1,13 +1,14 @@
1
- import type { SequenceSchema } from 'remotion';
1
+ import type { SequenceNodePath, SequenceSchema } from 'remotion';
2
2
  import { callApi } from '../call-api';
3
3
  type SubscribeResult = Awaited<ReturnType<typeof callApi<'/api/subscribe-to-sequence-props'>>>;
4
4
  type ApplyResult = (result: SubscribeResult) => void;
5
- export declare const acquireSequencePropsSubscription: ({ fileName, line, column, schema, effects, clientId, applyOnce, applyEach, }: {
5
+ export declare const acquireSequencePropsSubscription: ({ fileName, line, column, schema, effects, nodePath, clientId, applyOnce, applyEach, }: {
6
6
  fileName: string;
7
7
  line: number;
8
8
  column: number;
9
9
  schema: SequenceSchema;
10
10
  effects: SequenceSchema[];
11
+ nodePath: SequenceNodePath | null;
11
12
  clientId: string;
12
13
  applyOnce: ApplyResult;
13
14
  applyEach: ApplyResult;
@@ -6,7 +6,7 @@ const remotion_1 = require("remotion");
6
6
  const call_api_1 = require("../call-api");
7
7
  const makeKey = (fileName, line, column, sequenceKeys, effectKeys) => `${fileName}\0${line}\0${column}\0${sequenceKeys.join('\0')}\0${effectKeys.map((keys) => keys.join('\0')).join('\0\0')}`;
8
8
  const entries = new Map();
9
- const acquireSequencePropsSubscription = ({ fileName, line, column, schema, effects, clientId, applyOnce, applyEach, }) => {
9
+ const acquireSequencePropsSubscription = ({ fileName, line, column, schema, effects, nodePath, clientId, applyOnce, applyEach, }) => {
10
10
  const sequenceKeys = (0, studio_shared_1.getAllSchemaKeys)(schema);
11
11
  const effectKeys = effects.map((effect) => (0, studio_shared_1.getAllSchemaKeys)(effect));
12
12
  const key = makeKey(fileName, line, column, sequenceKeys, effectKeys);
@@ -16,6 +16,7 @@ const acquireSequencePropsSubscription = ({ fileName, line, column, schema, effe
16
16
  fileName,
17
17
  line,
18
18
  column,
19
+ nodePath,
19
20
  keys: (0, studio_shared_1.getAllSchemaKeys)(schema),
20
21
  effects: effectKeys,
21
22
  clientId,
@@ -1,6 +1,7 @@
1
1
  export declare const TIMELINE_ROW_BASE_PADDING = 5;
2
2
  export declare const TIMELINE_EYE_COLUMN_WIDTH = 22;
3
3
  export declare const TIMELINE_ARROW_COLUMN_WIDTH = 16;
4
+ export declare const TIMELINE_KEYFRAME_CONTROLS_WIDTH = 38;
4
5
  export declare const TIMELINE_FIELD_LABEL_EXTRA_WIDTH = 15;
5
6
  export declare const getTimelineRowIndentWidth: (depth: number) => number;
6
7
  export declare const getTimelineRowLeftChromeWidth: (depth: number) => number;
@@ -1,10 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getExpandedRowDepth = exports.getTimelineFieldLabelFlexBasis = exports.getTimelineRowLeftChromeWidth = exports.getTimelineRowIndentWidth = exports.TIMELINE_FIELD_LABEL_EXTRA_WIDTH = exports.TIMELINE_ARROW_COLUMN_WIDTH = exports.TIMELINE_EYE_COLUMN_WIDTH = exports.TIMELINE_ROW_BASE_PADDING = void 0;
3
+ exports.getExpandedRowDepth = exports.getTimelineFieldLabelFlexBasis = exports.getTimelineRowLeftChromeWidth = exports.getTimelineRowIndentWidth = exports.TIMELINE_FIELD_LABEL_EXTRA_WIDTH = exports.TIMELINE_KEYFRAME_CONTROLS_WIDTH = exports.TIMELINE_ARROW_COLUMN_WIDTH = exports.TIMELINE_EYE_COLUMN_WIDTH = exports.TIMELINE_ROW_BASE_PADDING = void 0;
4
4
  const timeline_indent_1 = require("./timeline-indent");
5
5
  exports.TIMELINE_ROW_BASE_PADDING = 5;
6
6
  exports.TIMELINE_EYE_COLUMN_WIDTH = 22;
7
7
  exports.TIMELINE_ARROW_COLUMN_WIDTH = 16;
8
+ exports.TIMELINE_KEYFRAME_CONTROLS_WIDTH = 38;
8
9
  exports.TIMELINE_FIELD_LABEL_EXTRA_WIDTH = 15;
9
10
  const getTimelineRowIndentWidth = (depth) => {
10
11
  return depth * timeline_indent_1.TIMELINE_INDENT;
@@ -172,7 +172,7 @@ const getFrameIncrement = (durationInFrames) => {
172
172
  return (0, exports.getFrameIncrementFromWidth)(durationInFrames, width);
173
173
  };
174
174
  const getFrameIncrementFromWidth = (durationInFrames, width) => {
175
- return (width - timeline_layout_1.TIMELINE_PADDING * 2) / (durationInFrames - 1);
175
+ return (width - timeline_layout_1.TIMELINE_PADDING * 2) / durationInFrames;
176
176
  };
177
177
  exports.getFrameIncrementFromWidth = getFrameIncrementFromWidth;
178
178
  const getFrameWhileScrollingRight = ({ durationInFrames, width, }) => {
@@ -193,10 +193,10 @@ const getFrameWhileScrollingRight = ({ durationInFrames, width, }) => {
193
193
  exports.getFrameWhileScrollingRight = getFrameWhileScrollingRight;
194
194
  const getFrameFromX = ({ clientX, durationInFrames, width, extrapolate, }) => {
195
195
  const pos = clientX - timeline_layout_1.TIMELINE_PADDING;
196
- const frame = Math.round((0, remotion_1.interpolate)(pos, [0, width - timeline_layout_1.TIMELINE_PADDING * 2], [0, durationInFrames - 1], {
196
+ const frame = Math.min(durationInFrames - 1, Math.round((0, remotion_1.interpolate)(pos, [0, width - timeline_layout_1.TIMELINE_PADDING * 2], [0, durationInFrames], {
197
197
  extrapolateLeft: extrapolate,
198
198
  extrapolateRight: extrapolate,
199
- }));
199
+ })));
200
200
  return frame;
201
201
  };
202
202
  exports.getFrameFromX = getFrameFromX;
@@ -0,0 +1,2 @@
1
+ export declare const parseTranslate: (value: string) => [number, number];
2
+ export declare const serializeTranslate: (x: number, y: number) => string;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.serializeTranslate = exports.parseTranslate = void 0;
4
+ const PIXEL_PATTERN = /^(-?\d+(?:\.\d+)?)px(?:\s+(-?\d+(?:\.\d+)?)px)?$/;
5
+ const parseTranslate = (value) => {
6
+ const m = value.match(PIXEL_PATTERN);
7
+ if (!m) {
8
+ return [0, 0];
9
+ }
10
+ return [Number(m[1]), m[2] !== undefined ? Number(m[2]) : 0];
11
+ };
12
+ exports.parseTranslate = parseTranslate;
13
+ const formatTranslateCoordinate = (value) => {
14
+ const rounded = Math.round(value * 1000) / 1000;
15
+ return String(Object.is(rounded, -0) ? 0 : rounded);
16
+ };
17
+ const serializeTranslate = (x, y) => {
18
+ return `${formatTranslateCoordinate(x)}px ${formatTranslateCoordinate(y)}px`;
19
+ };
20
+ exports.serializeTranslate = serializeTranslate;
@@ -10,13 +10,20 @@ const get_node_keyframes_1 = require("./get-node-keyframes");
10
10
  const useExpandedTrackKeyframeRows = ({ sequence, nodePathInfo, keyframeDisplayOffset, }) => {
11
11
  const { getIsExpanded } = (0, react_1.useContext)(ExpandedTracksProvider_1.ExpandedTracksGetterContext);
12
12
  const { codeValues } = (0, react_1.useContext)(remotion_1.Internals.VisualModeCodeValuesContext);
13
+ const { getDragOverrides, getEffectDragOverrides } = (0, react_1.useContext)(remotion_1.Internals.VisualModeDragOverridesContext);
13
14
  const tree = (0, react_1.useMemo)(() => (0, timeline_layout_1.buildTimelineTree)({
14
15
  sequence,
15
16
  nodePathInfo,
16
- getDragOverrides: () => ({}),
17
- getEffectDragOverrides: () => ({}),
17
+ getDragOverrides,
18
+ getEffectDragOverrides,
18
19
  codeValues,
19
- }), [codeValues, nodePathInfo, sequence]);
20
+ }), [
21
+ codeValues,
22
+ getDragOverrides,
23
+ getEffectDragOverrides,
24
+ nodePathInfo,
25
+ sequence,
26
+ ]);
20
27
  const flat = (0, react_1.useMemo)(() => (0, timeline_layout_1.flattenVisibleTreeNodes)({ nodes: tree, getIsExpanded }), [tree, getIsExpanded]);
21
28
  const expandedHeight = (0, react_1.useMemo)(() => (0, timeline_layout_1.getExpandedTrackHeight)({
22
29
  sequence,
@@ -36,7 +36,7 @@ const useSequencePropsSubscription = ({ originalLocation, overrideId, schema, ef
36
36
  const locationLine = (_b = validatedLocation === null || validatedLocation === void 0 ? void 0 : validatedLocation.line) !== null && _b !== void 0 ? _b : null;
37
37
  const locationColumn = (_c = validatedLocation === null || validatedLocation === void 0 ? void 0 : validatedLocation.column) !== null && _c !== void 0 ? _c : null;
38
38
  (0, react_1.useEffect)(() => {
39
- var _a;
39
+ var _a, _b;
40
40
  if (!clientId ||
41
41
  !locationSource ||
42
42
  !locationLine ||
@@ -51,6 +51,7 @@ const useSequencePropsSubscription = ({ originalLocation, overrideId, schema, ef
51
51
  column: locationColumn,
52
52
  schema,
53
53
  effects,
54
+ nodePath: (_b = nodePathAtResubscribe === null || nodePathAtResubscribe === void 0 ? void 0 : nodePathAtResubscribe.nodePath) !== null && _b !== void 0 ? _b : null,
54
55
  clientId,
55
56
  applyOnce: (result) => {
56
57
  if (!result.success) {
@@ -2,12 +2,18 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getCompositionMenuItems = void 0;
4
4
  const no_react_1 = require("remotion/no-react");
5
+ const format_file_location_1 = require("../helpers/format-file-location");
5
6
  const open_in_editor_1 = require("../helpers/open-in-editor");
6
7
  const NotificationCenter_1 = require("./Notifications/NotificationCenter");
7
8
  const getCompositionMenuItems = ({ composition, connectionStatus, resolvedLocation, setSelectedModal, closeMenu, readOnlyStudio, }) => {
8
9
  const editorName = window.remotion_editorName;
10
+ const fileLocation = (0, format_file_location_1.formatFileLocation)({
11
+ location: resolvedLocation,
12
+ root: window.remotion_cwd,
13
+ });
9
14
  const showInEditorDisabled = !composition || connectionStatus !== 'connected' || !resolvedLocation;
10
15
  const openComponentInEditorDisabled = showInEditorDisabled || !(resolvedLocation === null || resolvedLocation === void 0 ? void 0 : resolvedLocation.source);
16
+ const copyFileLocationDisabled = !composition || !fileLocation;
11
17
  return [
12
18
  editorName
13
19
  ? {
@@ -34,6 +40,31 @@ const getCompositionMenuItems = ({ composition, connectionStatus, resolvedLocati
34
40
  disabled: showInEditorDisabled,
35
41
  }
36
42
  : null,
43
+ {
44
+ id: 'copy-file-location',
45
+ keyHint: null,
46
+ label: `Copy file location`,
47
+ leftItem: null,
48
+ onClick: () => {
49
+ closeMenu();
50
+ if (!fileLocation) {
51
+ return;
52
+ }
53
+ navigator.clipboard
54
+ .writeText(fileLocation)
55
+ .then(() => {
56
+ (0, NotificationCenter_1.showNotification)('Copied file location to clipboard', 1000);
57
+ })
58
+ .catch((err) => {
59
+ (0, NotificationCenter_1.showNotification)(`Could not copy to clipboard: ${err.message}`, 1000);
60
+ });
61
+ },
62
+ quickSwitcherLabel: 'Copy composition file location',
63
+ subMenu: null,
64
+ type: 'item',
65
+ value: 'copy-file-location',
66
+ disabled: copyFileLocationDisabled,
67
+ },
37
68
  editorName
38
69
  ? {
39
70
  id: 'open-component-in-editor',
@@ -62,7 +93,7 @@ const getCompositionMenuItems = ({ composition, connectionStatus, resolvedLocati
62
93
  disabled: openComponentInEditorDisabled,
63
94
  }
64
95
  : null,
65
- editorName
96
+ editorName || fileLocation
66
97
  ? {
67
98
  type: 'divider',
68
99
  id: 'show-in-editor-divider',
@@ -43,7 +43,6 @@ const OpenInEditor = ({ stack, canHaveKeyboardShortcuts }) => {
43
43
  const openInBrowser = (0, react_1.useCallback)(() => {
44
44
  dispatch({ type: 'start' });
45
45
  (0, open_in_editor_1.openInEditor)(stack)
46
- .then((res) => res.json())
47
46
  .then((data) => {
48
47
  if (data.success) {
49
48
  dispatchIfMounted({ type: 'succeed' });