@remotion/studio 4.0.476 → 4.0.477

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 (61) hide show
  1. package/dist/components/Canvas.js +40 -1
  2. package/dist/components/ControlButton.d.ts +1 -0
  3. package/dist/components/ControlButton.js +7 -2
  4. package/dist/components/EditorGuides/Guide.js +122 -20
  5. package/dist/components/EditorRuler/Ruler.js +21 -15
  6. package/dist/components/EditorRuler/index.js +18 -10
  7. package/dist/components/OutlineToggle.js +1 -1
  8. package/dist/components/SelectedOutlineElement.d.ts +17 -0
  9. package/dist/components/SelectedOutlineElement.js +938 -0
  10. package/dist/components/SelectedOutlineOverlay.d.ts +4 -210
  11. package/dist/components/SelectedOutlineOverlay.js +64 -1637
  12. package/dist/components/SelectedOutlineUvControls.js +1 -1
  13. package/dist/components/ShowGuidesProvider.js +4 -4
  14. package/dist/components/Timeline/SubscribeToNodePaths.d.ts +2 -1
  15. package/dist/components/Timeline/SubscribeToNodePaths.js +2 -1
  16. package/dist/components/Timeline/Timeline.js +3 -1
  17. package/dist/components/Timeline/TimelineClipboardKeybindings.js +9 -10
  18. package/dist/components/Timeline/TimelineDeleteKeybindings.js +15 -4
  19. package/dist/components/Timeline/TimelineKeyframeEasingLine.js +7 -11
  20. package/dist/components/Timeline/TimelineList.js +1 -1
  21. package/dist/components/Timeline/TimelineSelection.d.ts +27 -13
  22. package/dist/components/Timeline/TimelineSelection.js +47 -28
  23. package/dist/components/Timeline/TimelineSequence.js +169 -8
  24. package/dist/components/Timeline/TimelineSequenceFrame.d.ts +1 -0
  25. package/dist/components/Timeline/TimelineSequenceFrame.js +17 -6
  26. package/dist/components/Timeline/TimelineSequenceItem.d.ts +1 -0
  27. package/dist/components/Timeline/TimelineSequenceItem.js +90 -130
  28. package/dist/components/Timeline/delete-selected-timeline-item.js +4 -0
  29. package/dist/components/Timeline/duplicate-selected-timeline-item.d.ts +1 -2
  30. package/dist/components/Timeline/get-sequence-context-menu-items.d.ts +20 -0
  31. package/dist/components/Timeline/get-sequence-context-menu-items.js +160 -0
  32. package/dist/components/Timeline/sequence-props-subscription-store.d.ts +2 -1
  33. package/dist/components/Timeline/sequence-props-subscription-store.js +11 -3
  34. package/dist/components/Timeline/should-clear-selection-on-pointer-down.d.ts +2 -0
  35. package/dist/components/Timeline/should-clear-selection-on-pointer-down.js +16 -2
  36. package/dist/components/Timeline/update-selected-easing.d.ts +4 -6
  37. package/dist/components/Timeline/use-sequence-props-subscription.d.ts +2 -1
  38. package/dist/components/Timeline/use-sequence-props-subscription.js +3 -1
  39. package/dist/components/Timeline/use-timeline-keyframe-drag.d.ts +37 -1
  40. package/dist/components/Timeline/use-timeline-keyframe-drag.js +282 -1
  41. package/dist/components/import-assets.d.ts +36 -8
  42. package/dist/components/import-assets.js +170 -10
  43. package/dist/components/selected-outline-drag.d.ts +117 -0
  44. package/dist/components/selected-outline-drag.js +427 -0
  45. package/dist/components/selected-outline-measurement.d.ts +67 -0
  46. package/dist/components/selected-outline-measurement.js +355 -0
  47. package/dist/components/selected-outline-types.d.ts +121 -0
  48. package/dist/components/selected-outline-types.js +15 -0
  49. package/dist/components/selected-outline-uv.d.ts +1 -0
  50. package/dist/components/selected-outline-uv.js +12 -0
  51. package/dist/esm/{chunk-0atarw3p.js → chunk-t8fjnw2d.js} +12570 -11492
  52. package/dist/esm/internals.mjs +12570 -11492
  53. package/dist/esm/previewEntry.mjs +12580 -11502
  54. package/dist/esm/renderEntry.mjs +1 -1
  55. package/dist/helpers/editor-guide-selection.d.ts +31 -0
  56. package/dist/helpers/editor-guide-selection.js +58 -0
  57. package/dist/helpers/editor-ruler.d.ts +3 -3
  58. package/dist/helpers/editor-ruler.js +16 -18
  59. package/dist/state/editor-guides.d.ts +2 -2
  60. package/dist/state/editor-guides.js +2 -2
  61. package/package.json +11 -11
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSequenceContextMenuItems = void 0;
4
+ const no_react_1 = require("remotion/no-react");
5
+ const NotificationCenter_1 = require("../Notifications/NotificationCenter");
6
+ const timeline_asset_link_1 = require("./timeline-asset-link");
7
+ const normalizeMenuDividers = (items) => {
8
+ var _a;
9
+ const normalized = [];
10
+ for (const item of items) {
11
+ if (item.type === 'divider') {
12
+ const previousItem = normalized[normalized.length - 1];
13
+ if (!previousItem || previousItem.type === 'divider') {
14
+ continue;
15
+ }
16
+ }
17
+ normalized.push(item);
18
+ }
19
+ if (((_a = normalized[normalized.length - 1]) === null || _a === void 0 ? void 0 : _a.type) === 'divider') {
20
+ normalized.pop();
21
+ }
22
+ return normalized;
23
+ };
24
+ const getSequenceContextMenuItems = ({ assetLinkInfo, canOpenInEditor, deleteDisabled, disableInteractivityDisabled, duplicateDisabled, fileLocation, includeSourceEditItems, onDeleteSequenceFromSource, onDisableSequenceInteractivity, onDuplicateSequenceFromSource, openInEditor, originalLocation, selectAsset, sequence, sourceActions = [], }) => {
25
+ const editorName = window.remotion_editorName;
26
+ const { documentationLink } = sequence;
27
+ const items = [
28
+ editorName
29
+ ? {
30
+ type: 'item',
31
+ id: 'show-in-editor',
32
+ keyHint: null,
33
+ label: `Show in ${editorName}`,
34
+ leftItem: null,
35
+ disabled: !canOpenInEditor || !originalLocation,
36
+ onClick: openInEditor,
37
+ quickSwitcherLabel: null,
38
+ subMenu: null,
39
+ value: 'show-in-editor',
40
+ }
41
+ : null,
42
+ {
43
+ type: 'item',
44
+ id: 'copy-file-location',
45
+ keyHint: null,
46
+ label: 'Copy file location',
47
+ leftItem: null,
48
+ disabled: !fileLocation,
49
+ onClick: () => {
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: null,
63
+ subMenu: null,
64
+ value: 'copy-file-location',
65
+ },
66
+ documentationLink
67
+ ? {
68
+ type: 'item',
69
+ id: 'open-component-docs',
70
+ keyHint: null,
71
+ label: 'Open component docs',
72
+ leftItem: null,
73
+ disabled: false,
74
+ onClick: () => {
75
+ window.open(documentationLink, '_blank', 'noopener,noreferrer');
76
+ },
77
+ quickSwitcherLabel: null,
78
+ subMenu: null,
79
+ value: 'open-component-docs',
80
+ }
81
+ : null,
82
+ assetLinkInfo
83
+ ? {
84
+ type: 'item',
85
+ id: 'show-asset',
86
+ keyHint: null,
87
+ label: 'Show asset',
88
+ leftItem: null,
89
+ disabled: false,
90
+ onClick: () => {
91
+ (0, timeline_asset_link_1.openTimelineAssetLink)(assetLinkInfo, selectAsset);
92
+ },
93
+ quickSwitcherLabel: null,
94
+ subMenu: null,
95
+ value: 'show-asset',
96
+ }
97
+ : null,
98
+ documentationLink || assetLinkInfo
99
+ ? {
100
+ type: 'divider',
101
+ id: 'sequence-link-divider',
102
+ }
103
+ : null,
104
+ sourceActions.length > 0
105
+ ? {
106
+ type: 'divider',
107
+ id: 'sequence-source-actions-divider',
108
+ }
109
+ : null,
110
+ ...sourceActions,
111
+ {
112
+ type: 'item',
113
+ id: 'disable-interactivity',
114
+ keyHint: null,
115
+ label: 'Disable interactivity',
116
+ leftItem: null,
117
+ disabled: disableInteractivityDisabled,
118
+ onClick: onDisableSequenceInteractivity,
119
+ quickSwitcherLabel: null,
120
+ subMenu: null,
121
+ value: 'disable-interactivity',
122
+ },
123
+ includeSourceEditItems
124
+ ? {
125
+ type: 'item',
126
+ id: 'duplicate-sequence',
127
+ keyHint: null,
128
+ label: 'Duplicate',
129
+ leftItem: null,
130
+ disabled: duplicateDisabled,
131
+ onClick: onDuplicateSequenceFromSource,
132
+ quickSwitcherLabel: null,
133
+ subMenu: null,
134
+ value: 'duplicate-sequence',
135
+ }
136
+ : null,
137
+ includeSourceEditItems
138
+ ? {
139
+ type: 'divider',
140
+ id: 'sequence-duplicate-delete-divider',
141
+ }
142
+ : null,
143
+ includeSourceEditItems
144
+ ? {
145
+ type: 'item',
146
+ id: 'delete-sequence',
147
+ keyHint: null,
148
+ label: 'Delete',
149
+ leftItem: null,
150
+ disabled: deleteDisabled,
151
+ onClick: onDeleteSequenceFromSource,
152
+ quickSwitcherLabel: null,
153
+ subMenu: null,
154
+ value: 'delete-sequence',
155
+ }
156
+ : null,
157
+ ].filter(no_react_1.NoReactInternals.truthy);
158
+ return normalizeMenuDividers(items);
159
+ };
160
+ exports.getSequenceContextMenuItems = getSequenceContextMenuItems;
@@ -2,11 +2,12 @@ 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, nodePath, clientId, applyOnce, applyEach, }: {
5
+ export declare const acquireSequencePropsSubscription: ({ fileName, line, column, schema, componentIdentity, effects, nodePath, clientId, applyOnce, applyEach, }: {
6
6
  fileName: string;
7
7
  line: number;
8
8
  column: number;
9
9
  schema: SequenceSchema;
10
+ componentIdentity: string | null;
10
11
  effects: SequenceSchema[];
11
12
  nodePath: SequenceNodePath | null;
12
13
  clientId: string;
@@ -4,12 +4,19 @@ exports.acquireSequencePropsSubscription = void 0;
4
4
  const studio_shared_1 = require("@remotion/studio-shared");
5
5
  const remotion_1 = require("remotion");
6
6
  const call_api_1 = require("../call-api");
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')}`;
7
+ const makeKey = ({ fileName, line, column, componentIdentity, sequenceKeys, effectKeys, }) => `${fileName}\0${line}\0${column}\0${componentIdentity !== null && componentIdentity !== void 0 ? componentIdentity : ''}\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, nodePath, clientId, applyOnce, applyEach, }) => {
9
+ const acquireSequencePropsSubscription = ({ fileName, line, column, schema, componentIdentity, 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
- const key = makeKey(fileName, line, column, sequenceKeys, effectKeys);
12
+ const key = makeKey({
13
+ fileName,
14
+ line,
15
+ column,
16
+ componentIdentity,
17
+ sequenceKeys,
18
+ effectKeys,
19
+ });
13
20
  let entry = entries.get(key);
14
21
  if (!entry) {
15
22
  const promise = (0, call_api_1.callApi)('/api/subscribe-to-sequence-props', {
@@ -17,6 +24,7 @@ const acquireSequencePropsSubscription = ({ fileName, line, column, schema, effe
17
24
  line,
18
25
  column,
19
26
  nodePath,
27
+ componentIdentity,
20
28
  keys: (0, studio_shared_1.getAllSchemaKeys)(schema),
21
29
  effects: effectKeys,
22
30
  clientId,
@@ -1,3 +1,5 @@
1
+ export declare const PREVENT_CLEAR_SELECTION_ON_POINTER_DOWN_ATTR = "data-remotion-prevent-selection-clear";
1
2
  export declare const shouldClearSelectionOnPointerDown: (event: {
2
3
  readonly button: number;
4
+ readonly target?: EventTarget | null | undefined;
3
5
  }) => boolean;
@@ -1,7 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.shouldClearSelectionOnPointerDown = void 0;
3
+ exports.shouldClearSelectionOnPointerDown = exports.PREVENT_CLEAR_SELECTION_ON_POINTER_DOWN_ATTR = void 0;
4
+ exports.PREVENT_CLEAR_SELECTION_ON_POINTER_DOWN_ATTR = 'data-remotion-prevent-selection-clear';
5
+ const isSelectionClearBlocked = (target) => {
6
+ if (target === null ||
7
+ target === undefined ||
8
+ typeof target !== 'object' ||
9
+ !('closest' in target) ||
10
+ typeof target.closest !== 'function') {
11
+ return false;
12
+ }
13
+ return (target.closest(`[${exports.PREVENT_CLEAR_SELECTION_ON_POINTER_DOWN_ATTR}]`) !== null);
14
+ };
4
15
  const shouldClearSelectionOnPointerDown = (event) => {
5
- return event.button === 0;
16
+ if (event.button !== 0) {
17
+ return false;
18
+ }
19
+ return !isSelectionClearBlocked(event.target);
6
20
  };
7
21
  exports.shouldClearSelectionOnPointerDown = shouldClearSelectionOnPointerDown;
@@ -3,19 +3,17 @@ import type { SetPropStatuses } from './save-sequence-prop';
3
3
  import type { TimelineEasingSelection, TimelineSelection } from './TimelineSelection';
4
4
  export type EasingSelection = TimelineEasingSelection;
5
5
  export type TimelineEasingValue = 'linear' | [number, number, number, number];
6
- export declare const getEasingSelections: (selections: readonly TimelineSelection[]) => ({
7
- readonly nodePathInfo: import("../../helpers/get-timeline-sequence-sort-key").SequenceNodePathInfo;
8
- } & {
6
+ export declare const getEasingSelections: (selections: readonly TimelineSelection[]) => {
9
7
  readonly type: "easing";
8
+ readonly nodePathInfo: import("../../helpers/get-timeline-sequence-sort-key").SequenceNodePathInfo;
10
9
  readonly fromFrame: number;
11
10
  readonly toFrame: number;
12
11
  readonly segmentIndex: number;
13
- })[];
12
+ }[];
14
13
  export declare const getTimelineEasingValueForSelection: ({ selection, sequences, overrideIdsToNodePaths, propStatuses, }: {
15
14
  selection: {
16
- readonly nodePathInfo: import("../../helpers/get-timeline-sequence-sort-key").SequenceNodePathInfo;
17
- } & {
18
15
  readonly type: "easing";
16
+ readonly nodePathInfo: import("../../helpers/get-timeline-sequence-sort-key").SequenceNodePathInfo;
19
17
  readonly fromFrame: number;
20
18
  readonly toFrame: number;
21
19
  readonly segmentIndex: number;
@@ -1,7 +1,8 @@
1
1
  import type { SequenceSchema } from 'remotion';
2
2
  import type { OriginalPosition } from '../../error-overlay/react-overlay/utils/get-source-map';
3
- export declare const useSequencePropsSubscription: ({ originalLocation, overrideId, schema, effects, }: {
3
+ export declare const useSequencePropsSubscription: ({ originalLocation, overrideId, componentIdentity, schema, effects, }: {
4
4
  overrideId: string;
5
+ componentIdentity: string | null;
5
6
  schema: SequenceSchema;
6
7
  effects: SequenceSchema[];
7
8
  originalLocation: OriginalPosition | null;
@@ -7,7 +7,7 @@ const remotion_1 = require("remotion");
7
7
  const client_id_1 = require("../../helpers/client-id");
8
8
  const ExpandedTracksProvider_1 = require("../ExpandedTracksProvider");
9
9
  const sequence_props_subscription_store_1 = require("./sequence-props-subscription-store");
10
- const useSequencePropsSubscription = ({ originalLocation, overrideId, schema, effects, }) => {
10
+ const useSequencePropsSubscription = ({ originalLocation, overrideId, componentIdentity, schema, effects, }) => {
11
11
  var _a, _b, _c;
12
12
  const { setPropStatuses } = (0, react_1.useContext)(remotion_1.Internals.VisualModeSettersContext);
13
13
  const { setOverrideIdToNodePath } = (0, react_1.useContext)(remotion_1.Internals.OverrideIdsToNodePathsSettersContext);
@@ -50,6 +50,7 @@ const useSequencePropsSubscription = ({ originalLocation, overrideId, schema, ef
50
50
  line: locationLine,
51
51
  column: locationColumn,
52
52
  schema,
53
+ componentIdentity,
53
54
  effects,
54
55
  nodePath: (_b = nodePathAtResubscribe === null || nodePathAtResubscribe === void 0 ? void 0 : nodePathAtResubscribe.nodePath) !== null && _b !== void 0 ? _b : null,
55
56
  clientId,
@@ -85,6 +86,7 @@ const useSequencePropsSubscription = ({ originalLocation, overrideId, schema, ef
85
86
  };
86
87
  }, [
87
88
  clientId,
89
+ componentIdentity,
88
90
  effects,
89
91
  effectsSignature,
90
92
  locationColumn,
@@ -1,6 +1,30 @@
1
1
  import type React from 'react';
2
2
  import type { SequenceNodePathInfo } from '../../helpers/get-timeline-sequence-sort-key';
3
- import { type TimelineSelectionInteraction } from './TimelineSelection';
3
+ import { type TimelineDraggedKeyframe } from './TimelineKeyframeDragState';
4
+ import { type TimelineSelection, type TimelineSelectionInteraction } from './TimelineSelection';
5
+ export declare const getKeyframesForTimelineEasingDrag: ({ currentSelections, interaction, selectionItem, selected, }: {
6
+ readonly currentSelections: readonly TimelineSelection[];
7
+ readonly interaction: TimelineSelectionInteraction;
8
+ readonly selectionItem: {
9
+ readonly type: "easing";
10
+ readonly nodePathInfo: SequenceNodePathInfo;
11
+ readonly fromFrame: number;
12
+ readonly toFrame: number;
13
+ readonly segmentIndex: number;
14
+ };
15
+ readonly selected: boolean;
16
+ }) => ({
17
+ readonly type: "keyframe";
18
+ readonly nodePathInfo: SequenceNodePathInfo;
19
+ readonly frame: number;
20
+ } & {
21
+ type: "keyframe";
22
+ })[];
23
+ export declare const getTimelineSelectionsAfterEasingKeyframeDrag: ({ delta, selections, targets, }: {
24
+ readonly delta: number;
25
+ readonly selections: readonly TimelineSelection[];
26
+ readonly targets: readonly TimelineDraggedKeyframe[];
27
+ }) => TimelineSelection[];
4
28
  export declare const useTimelineKeyframeDrag: ({ frame, nodePathInfo, onSelect, selectable, selected, }: {
5
29
  readonly frame: number;
6
30
  readonly nodePathInfo: SequenceNodePathInfo;
@@ -8,3 +32,15 @@ export declare const useTimelineKeyframeDrag: ({ frame, nodePathInfo, onSelect,
8
32
  readonly selectable: boolean;
9
33
  readonly selected: boolean;
10
34
  }) => (e: React.PointerEvent<HTMLButtonElement>) => void;
35
+ export declare const useTimelineEasingKeyframeDrag: ({ onSelect, selectable, selected, selectionItem, }: {
36
+ readonly onSelect: (interaction?: TimelineSelectionInteraction | undefined) => void;
37
+ readonly selectable: boolean;
38
+ readonly selected: boolean;
39
+ readonly selectionItem: {
40
+ readonly type: "easing";
41
+ readonly nodePathInfo: SequenceNodePathInfo;
42
+ readonly fromFrame: number;
43
+ readonly toFrame: number;
44
+ readonly segmentIndex: number;
45
+ };
46
+ }) => (e: React.PointerEvent<HTMLButtonElement>) => void;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useTimelineKeyframeDrag = void 0;
3
+ exports.useTimelineEasingKeyframeDrag = exports.useTimelineKeyframeDrag = exports.getTimelineSelectionsAfterEasingKeyframeDrag = exports.getKeyframesForTimelineEasingDrag = void 0;
4
4
  const studio_shared_1 = require("@remotion/studio-shared");
5
5
  const react_1 = require("react");
6
6
  const remotion_1 = require("remotion");
@@ -15,6 +15,89 @@ const TimelineSelection_1 = require("./TimelineSelection");
15
15
  const TimelineWidthProvider_1 = require("./TimelineWidthProvider");
16
16
  const pointerDragThreshold = 3;
17
17
  const isKeyframeSelection = (selection) => selection.type === 'keyframe';
18
+ const isEasingSelection = (selection) => selection.type === 'easing';
19
+ const keyframesForEasing = (easing) => [
20
+ {
21
+ type: 'keyframe',
22
+ nodePathInfo: easing.nodePathInfo,
23
+ frame: easing.fromFrame,
24
+ },
25
+ {
26
+ type: 'keyframe',
27
+ nodePathInfo: easing.nodePathInfo,
28
+ frame: easing.toFrame,
29
+ },
30
+ ];
31
+ const deduplicateKeyframeSelections = (keyframes) => {
32
+ const seen = new Set();
33
+ return keyframes.filter((keyframe) => {
34
+ const key = JSON.stringify({
35
+ nodePathInfo: keyframe.nodePathInfo,
36
+ frame: keyframe.frame,
37
+ });
38
+ if (seen.has(key)) {
39
+ return false;
40
+ }
41
+ seen.add(key);
42
+ return true;
43
+ });
44
+ };
45
+ const getKeyframesForTimelineEasingDrag = ({ currentSelections, interaction, selectionItem, selected, }) => {
46
+ if (interaction.shiftKey || interaction.toggleKey) {
47
+ return keyframesForEasing(selectionItem);
48
+ }
49
+ const selectedKeyframes = currentSelections.filter(isKeyframeSelection);
50
+ if (selectedKeyframes.length > 0) {
51
+ return selectedKeyframes;
52
+ }
53
+ if (!selected) {
54
+ return keyframesForEasing(selectionItem);
55
+ }
56
+ const selectedEasings = currentSelections.filter(isEasingSelection);
57
+ return deduplicateKeyframeSelections((selectedEasings.length > 0 ? selectedEasings : [selectionItem]).flatMap(keyframesForEasing));
58
+ };
59
+ exports.getKeyframesForTimelineEasingDrag = getKeyframesForTimelineEasingDrag;
60
+ const getTimelineSelectionsAfterEasingKeyframeDrag = ({ delta, selections, targets, }) => {
61
+ const movedFrames = new Map(targets.map((target) => [
62
+ (0, TimelineKeyframeDragState_1.getTimelineKeyframeDragKey)(target),
63
+ target.frame + delta,
64
+ ]));
65
+ return selections.map((selection) => {
66
+ var _a, _b;
67
+ if (selection.type === 'keyframe') {
68
+ const movedFrame = movedFrames.get((0, TimelineKeyframeDragState_1.getTimelineKeyframeDragKey)({
69
+ nodePathInfo: selection.nodePathInfo,
70
+ frame: selection.frame,
71
+ }));
72
+ return movedFrame === undefined
73
+ ? selection
74
+ : {
75
+ ...selection,
76
+ frame: movedFrame,
77
+ };
78
+ }
79
+ if (selection.type === 'easing') {
80
+ const movedFromFrame = (_a = movedFrames.get((0, TimelineKeyframeDragState_1.getTimelineKeyframeDragKey)({
81
+ nodePathInfo: selection.nodePathInfo,
82
+ frame: selection.fromFrame,
83
+ }))) !== null && _a !== void 0 ? _a : selection.fromFrame;
84
+ const movedToFrame = (_b = movedFrames.get((0, TimelineKeyframeDragState_1.getTimelineKeyframeDragKey)({
85
+ nodePathInfo: selection.nodePathInfo,
86
+ frame: selection.toFrame,
87
+ }))) !== null && _b !== void 0 ? _b : selection.toFrame;
88
+ return movedFromFrame === selection.fromFrame &&
89
+ movedToFrame === selection.toFrame
90
+ ? selection
91
+ : {
92
+ ...selection,
93
+ fromFrame: movedFromFrame,
94
+ toFrame: movedToFrame,
95
+ };
96
+ }
97
+ return selection;
98
+ });
99
+ };
100
+ exports.getTimelineSelectionsAfterEasingKeyframeDrag = getTimelineSelectionsAfterEasingKeyframeDrag;
18
101
  const getCodeValueForTarget = ({ propStatuses, nodePath, }) => propStatuses[remotion_1.Internals.makeSequencePropsSubscriptionKey(nodePath)];
19
102
  const getTimelineKeyframeDragTarget = ({ propStatuses, displayFrame, nodePathInfo, overrideIdsToNodePaths, sequences, }) => {
20
103
  var _a, _b;
@@ -373,3 +456,201 @@ const useTimelineKeyframeDrag = ({ frame, nodePathInfo, onSelect, selectable, se
373
456
  ]);
374
457
  };
375
458
  exports.useTimelineKeyframeDrag = useTimelineKeyframeDrag;
459
+ const useTimelineEasingKeyframeDrag = ({ onSelect, selectable, selected, selectionItem, }) => {
460
+ const videoConfig = (0, remotion_1.useVideoConfig)();
461
+ const timelineWidth = (0, react_1.useContext)(TimelineWidthProvider_1.TimelineWidthContext);
462
+ const { previewServerState } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
463
+ const sequencesRef = (0, react_1.useContext)(remotion_1.Internals.SequenceManagerRefContext);
464
+ const { overrideIdToNodePathMappings } = (0, react_1.useContext)(remotion_1.Internals.OverrideIdsToNodePathsGettersContext);
465
+ const propStatusesRef = (0, react_1.useContext)(remotion_1.Internals.VisualModePropStatusesRefContext);
466
+ const { clearDragOverrides, clearEffectDragOverrides, setPropStatuses, setDragOverrides, setEffectDragOverrides, } = (0, react_1.useContext)(remotion_1.Internals.VisualModeSettersContext);
467
+ const currentSelection = (0, TimelineSelection_1.useCurrentTimelineSelectionStateAsRef)();
468
+ const { clearDraggedKeyframes } = (0, TimelineKeyframeDragState_1.useTimelineKeyframeDragState)();
469
+ return (0, react_1.useCallback)((e) => {
470
+ if (e.button !== 0 || !selectable) {
471
+ return;
472
+ }
473
+ e.preventDefault();
474
+ e.stopPropagation();
475
+ const interaction = {
476
+ shiftKey: e.shiftKey,
477
+ toggleKey: e.metaKey || e.ctrlKey,
478
+ };
479
+ const shouldSelectOnPointerDown = !selected && !interaction.shiftKey && !interaction.toggleKey;
480
+ if (timelineWidth === null || previewServerState.type !== 'connected') {
481
+ onSelect(interaction);
482
+ return;
483
+ }
484
+ const keyframesToDrag = (0, exports.getKeyframesForTimelineEasingDrag)({
485
+ currentSelections: currentSelection.current.selectedItems,
486
+ interaction,
487
+ selectionItem,
488
+ selected,
489
+ });
490
+ if (shouldSelectOnPointerDown) {
491
+ onSelect(interaction);
492
+ }
493
+ const startClientX = e.clientX;
494
+ let dragTargets = null;
495
+ let hasDragged = false;
496
+ let lastDelta = 0;
497
+ const propStatuses = propStatusesRef.current;
498
+ const sequences = sequencesRef.current;
499
+ const resolveDragTargets = () => {
500
+ if (dragTargets !== null) {
501
+ return dragTargets;
502
+ }
503
+ dragTargets = keyframesToDrag
504
+ .map((keyframe) => getTimelineKeyframeDragTarget({
505
+ propStatuses,
506
+ displayFrame: keyframe.frame,
507
+ nodePathInfo: keyframe.nodePathInfo,
508
+ overrideIdsToNodePaths: overrideIdToNodePathMappings,
509
+ sequences,
510
+ }))
511
+ .filter((target) => target !== null);
512
+ return dragTargets;
513
+ };
514
+ const cleanup = () => {
515
+ window.removeEventListener('pointermove', onPointerMove);
516
+ window.removeEventListener('pointerup', onPointerUp);
517
+ window.removeEventListener('pointercancel', onPointerCancel);
518
+ };
519
+ const clearActiveOverrides = () => {
520
+ const targets = dragTargets;
521
+ if (targets === null) {
522
+ return;
523
+ }
524
+ clearDragOverridesForTargets({
525
+ clearDragOverrides,
526
+ clearEffectDragOverrides,
527
+ targets,
528
+ });
529
+ };
530
+ const onPointerMove = (event) => {
531
+ const clientXDelta = event.clientX - startClientX;
532
+ if (!hasDragged && Math.abs(clientXDelta) < pointerDragThreshold) {
533
+ return;
534
+ }
535
+ const targets = resolveDragTargets();
536
+ if (targets.length === 0) {
537
+ cleanup();
538
+ clearDraggedKeyframes();
539
+ return;
540
+ }
541
+ const rawDelta = getFrameDelta({
542
+ clientXDelta,
543
+ durationInFrames: videoConfig.durationInFrames,
544
+ timelineWidth,
545
+ });
546
+ const delta = (0, get_bounded_keyframe_drag_delta_1.getBoundedKeyframeDragDelta)({
547
+ delta: rawDelta,
548
+ durationInFrames: videoConfig.durationInFrames,
549
+ targets,
550
+ });
551
+ if (hasDragged && delta === lastDelta) {
552
+ return;
553
+ }
554
+ hasDragged = true;
555
+ lastDelta = delta;
556
+ if (!canMoveTimelineKeyframeDragTargets({ targets, delta })) {
557
+ clearActiveOverrides();
558
+ clearDraggedKeyframes();
559
+ return;
560
+ }
561
+ applyDragOverrides({
562
+ delta,
563
+ setDragOverrides,
564
+ setEffectDragOverrides,
565
+ targets,
566
+ });
567
+ };
568
+ const onPointerUp = () => {
569
+ cleanup();
570
+ const targets = dragTargets;
571
+ if (!hasDragged) {
572
+ if (!shouldSelectOnPointerDown) {
573
+ onSelect(interaction);
574
+ }
575
+ clearActiveOverrides();
576
+ clearDraggedKeyframes();
577
+ return;
578
+ }
579
+ if (lastDelta === 0 || targets === null) {
580
+ clearActiveOverrides();
581
+ clearDraggedKeyframes();
582
+ return;
583
+ }
584
+ if (!canMoveTimelineKeyframeDragTargets({
585
+ targets,
586
+ delta: lastDelta,
587
+ })) {
588
+ clearActiveOverrides();
589
+ clearDraggedKeyframes();
590
+ return;
591
+ }
592
+ currentSelection.current.selectItems((0, exports.getTimelineSelectionsAfterEasingKeyframeDrag)({
593
+ delta: lastDelta,
594
+ selections: currentSelection.current.selectedItems,
595
+ targets: targets.map((target) => ({
596
+ nodePathInfo: target.nodePathInfo,
597
+ frame: target.displayFrame,
598
+ })),
599
+ }));
600
+ clearActiveOverrides();
601
+ clearDraggedKeyframes();
602
+ (0, call_move_keyframe_1.callMoveKeyframes)({
603
+ sequenceKeyframes: targets
604
+ .filter((target) => target.type === 'sequence')
605
+ .map((target) => ({
606
+ fileName: target.fileName,
607
+ nodePath: target.nodePath,
608
+ fieldKey: target.fieldKey,
609
+ fromFrame: target.sourceFrame,
610
+ toFrame: target.sourceFrame + lastDelta,
611
+ schema: target.schema,
612
+ })),
613
+ effectKeyframes: targets
614
+ .filter((target) => target.type === 'effect')
615
+ .map((target) => ({
616
+ fileName: target.fileName,
617
+ nodePath: target.nodePath,
618
+ effectIndex: target.effectIndex,
619
+ fieldKey: target.fieldKey,
620
+ fromFrame: target.sourceFrame,
621
+ toFrame: target.sourceFrame + lastDelta,
622
+ schema: target.schema,
623
+ })),
624
+ setPropStatuses,
625
+ clientId: previewServerState.clientId,
626
+ }).catch(() => undefined);
627
+ };
628
+ const onPointerCancel = () => {
629
+ cleanup();
630
+ clearActiveOverrides();
631
+ clearDraggedKeyframes();
632
+ };
633
+ window.addEventListener('pointermove', onPointerMove);
634
+ window.addEventListener('pointerup', onPointerUp);
635
+ window.addEventListener('pointercancel', onPointerCancel);
636
+ }, [
637
+ clearDragOverrides,
638
+ clearEffectDragOverrides,
639
+ clearDraggedKeyframes,
640
+ currentSelection,
641
+ onSelect,
642
+ overrideIdToNodePathMappings,
643
+ propStatusesRef,
644
+ previewServerState,
645
+ selectable,
646
+ selected,
647
+ selectionItem,
648
+ sequencesRef,
649
+ setPropStatuses,
650
+ setDragOverrides,
651
+ setEffectDragOverrides,
652
+ timelineWidth,
653
+ videoConfig.durationInFrames,
654
+ ]);
655
+ };
656
+ exports.useTimelineEasingKeyframeDrag = useTimelineEasingKeyframeDrag;