@remotion/studio 4.0.472 → 4.0.473

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 (133) hide show
  1. package/dist/api/rename-static-file.d.ts +6 -0
  2. package/dist/api/rename-static-file.js +18 -0
  3. package/dist/components/AssetSelector.js +45 -4
  4. package/dist/components/AssetSelectorItem.js +153 -27
  5. package/dist/components/Canvas.js +60 -11
  6. package/dist/components/ConfirmationDialog-types.d.ts +8 -0
  7. package/dist/components/ConfirmationDialog-types.js +2 -0
  8. package/dist/components/ConfirmationDialog.d.ts +7 -0
  9. package/dist/components/ConfirmationDialog.js +103 -0
  10. package/dist/components/ContextMenu.d.ts +9 -1
  11. package/dist/components/ContextMenu.js +49 -5
  12. package/dist/components/CurrentAsset.d.ts +1 -0
  13. package/dist/components/CurrentAsset.js +13 -2
  14. package/dist/components/EditorContent.js +15 -2
  15. package/dist/components/EditorContexts.js +2 -1
  16. package/dist/components/EditorRuler/Ruler.js +2 -0
  17. package/dist/components/ExplorerPanel.d.ts +0 -4
  18. package/dist/components/ExplorerPanel.js +8 -4
  19. package/dist/components/ExplorerPanelRef.d.ts +4 -0
  20. package/dist/components/ExplorerPanelRef.js +5 -0
  21. package/dist/components/FilePreview.d.ts +1 -1
  22. package/dist/components/InitialCompositionLoader.d.ts +0 -1
  23. package/dist/components/InitialCompositionLoader.js +5 -27
  24. package/dist/components/Menu/MenuItem.js +7 -1
  25. package/dist/components/Menu/SubMenu.js +5 -1
  26. package/dist/components/Menu/portals.js +17 -8
  27. package/dist/components/MenuToolbar.js +5 -1
  28. package/dist/components/ModalContainer.js +6 -1
  29. package/dist/components/Modals.js +6 -2
  30. package/dist/components/NewComposition/ComboBox.js +8 -2
  31. package/dist/components/NewComposition/DeleteStaticFile.d.ts +4 -0
  32. package/dist/components/NewComposition/DeleteStaticFile.js +44 -0
  33. package/dist/components/NewComposition/RenameStaticFile.d.ts +4 -0
  34. package/dist/components/NewComposition/RenameStaticFile.js +118 -0
  35. package/dist/components/OptionsPanel.js +5 -1
  36. package/dist/components/OutlineToggle.d.ts +2 -0
  37. package/dist/components/OutlineToggle.js +20 -0
  38. package/dist/components/Preview.d.ts +0 -2
  39. package/dist/components/Preview.js +23 -33
  40. package/dist/components/PreviewToolbar.js +19 -6
  41. package/dist/components/RenderButton.js +8 -2
  42. package/dist/components/RenderPreview.js +2 -2
  43. package/dist/components/SelectedOutlineOverlay.d.ts +24 -0
  44. package/dist/components/SelectedOutlineOverlay.js +190 -22
  45. package/dist/components/ShowOutlinesProvider.d.ts +4 -0
  46. package/dist/components/ShowOutlinesProvider.js +24 -0
  47. package/dist/components/SizeSelector.js +3 -3
  48. package/dist/components/Splitter/SplitterHandle.js +2 -0
  49. package/dist/components/StaticFilePreview.js +2 -2
  50. package/dist/components/Timeline/KeyframeSettingsModal.d.ts +15 -0
  51. package/dist/components/Timeline/KeyframeSettingsModal.js +150 -0
  52. package/dist/components/Timeline/Timeline.js +3 -13
  53. package/dist/components/Timeline/TimelineClipboardKeybindings.d.ts +25 -2
  54. package/dist/components/Timeline/TimelineClipboardKeybindings.js +234 -20
  55. package/dist/components/Timeline/TimelineDeleteKeybindings.js +12 -2
  56. package/dist/components/Timeline/TimelineEffectItem.js +1 -0
  57. package/dist/components/Timeline/TimelineEffectPropItem.js +52 -2
  58. package/dist/components/Timeline/TimelineKeyframeControls.js +5 -15
  59. package/dist/components/Timeline/TimelineKeyframeDiamond.js +24 -21
  60. package/dist/components/Timeline/TimelineKeyframeDiamondIcon.d.ts +6 -0
  61. package/dist/components/Timeline/TimelineKeyframeDiamondIcon.js +14 -0
  62. package/dist/components/Timeline/TimelineKeyframeDragState.d.ts +17 -0
  63. package/dist/components/Timeline/TimelineKeyframeDragState.js +39 -0
  64. package/dist/components/Timeline/TimelineList.js +2 -2
  65. package/dist/components/Timeline/TimelineMediaInfo.d.ts +0 -13
  66. package/dist/components/Timeline/TimelineMediaInfo.js +8 -73
  67. package/dist/components/Timeline/TimelineScaleField.js +1 -1
  68. package/dist/components/Timeline/TimelineSequenceItem.d.ts +1 -0
  69. package/dist/components/Timeline/TimelineSequenceItem.js +276 -22
  70. package/dist/components/Timeline/TimelineSequencePropItem.js +81 -16
  71. package/dist/components/Timeline/TimelineSequenceRightEdgeDragHandle.js +25 -28
  72. package/dist/components/Timeline/apply-effect-response-to-code-values.d.ts +5 -0
  73. package/dist/components/Timeline/apply-effect-response-to-code-values.js +19 -0
  74. package/dist/components/Timeline/call-add-keyframe.js +2 -0
  75. package/dist/components/Timeline/call-move-keyframe.d.ts +19 -0
  76. package/dist/components/Timeline/call-move-keyframe.js +71 -0
  77. package/dist/components/Timeline/call-update-keyframe-settings.d.ts +22 -0
  78. package/dist/components/Timeline/call-update-keyframe-settings.js +52 -0
  79. package/dist/components/Timeline/delete-selected-timeline-item.d.ts +7 -4
  80. package/dist/components/Timeline/delete-selected-timeline-item.js +33 -21
  81. package/dist/components/Timeline/duplicate-selected-timeline-item.d.ts +4 -2
  82. package/dist/components/Timeline/duplicate-selected-timeline-item.js +39 -34
  83. package/dist/components/Timeline/get-bounded-keyframe-drag-delta.d.ts +8 -0
  84. package/dist/components/Timeline/get-bounded-keyframe-drag-delta.js +12 -0
  85. package/dist/components/Timeline/get-keyframe-navigation.d.ts +2 -2
  86. package/dist/components/Timeline/get-keyframe-navigation.js +14 -6
  87. package/dist/components/Timeline/reset-selected-timeline-props.js +3 -2
  88. package/dist/components/Timeline/save-effect-prop.d.ts +14 -3
  89. package/dist/components/Timeline/save-effect-prop.js +36 -18
  90. package/dist/components/Timeline/save-prop-queue.d.ts +2 -1
  91. package/dist/components/Timeline/save-prop-queue.js +5 -2
  92. package/dist/components/Timeline/save-sequence-prop.d.ts +2 -7
  93. package/dist/components/Timeline/save-sequence-prop.js +33 -30
  94. package/dist/components/Timeline/should-clear-selection-on-pointer-down.d.ts +3 -0
  95. package/dist/components/Timeline/should-clear-selection-on-pointer-down.js +7 -0
  96. package/dist/components/Timeline/timeline-asset-link.d.ts +13 -0
  97. package/dist/components/Timeline/timeline-asset-link.js +37 -0
  98. package/dist/components/Timeline/timeline-translate-utils.js +4 -1
  99. package/dist/components/Timeline/use-timeline-keyframe-drag.d.ts +10 -0
  100. package/dist/components/Timeline/use-timeline-keyframe-drag.js +378 -0
  101. package/dist/components/import-assets.d.ts +16 -0
  102. package/dist/components/import-assets.js +155 -18
  103. package/dist/components/load-canvas-content-from-url.d.ts +1 -0
  104. package/dist/components/load-canvas-content-from-url.js +9 -3
  105. package/dist/components/use-select-asset.d.ts +1 -0
  106. package/dist/components/use-select-asset.js +30 -0
  107. package/dist/error-overlay/error-origin.d.ts +3 -0
  108. package/dist/error-overlay/error-origin.js +42 -0
  109. package/dist/error-overlay/react-overlay/listen-to-runtime-errors.js +6 -2
  110. package/dist/error-overlay/remotion-overlay/ErrorLoader.js +38 -0
  111. package/dist/error-overlay/remotion-overlay/ShortcutHint.js +1 -1
  112. package/dist/error-overlay/remotion-overlay/log-studio-error.d.ts +3 -0
  113. package/dist/error-overlay/remotion-overlay/log-studio-error.js +27 -0
  114. package/dist/esm/{chunk-48grt472.js → chunk-q0jkt0zq.js} +21961 -19299
  115. package/dist/esm/internals.mjs +21961 -19299
  116. package/dist/esm/previewEntry.mjs +20600 -17914
  117. package/dist/esm/renderEntry.mjs +1 -1
  118. package/dist/helpers/get-asset-metadata.js +2 -2
  119. package/dist/helpers/get-preview-file-type.d.ts +2 -0
  120. package/dist/helpers/get-preview-file-type.js +33 -0
  121. package/dist/helpers/install-required-package.d.ts +1 -0
  122. package/dist/helpers/install-required-package.js +39 -0
  123. package/dist/helpers/remote-asset-drag.d.ts +4 -0
  124. package/dist/helpers/remote-asset-drag.js +73 -0
  125. package/dist/helpers/use-asset-drag-events.d.ts +5 -2
  126. package/dist/helpers/use-asset-drag-events.js +13 -2
  127. package/dist/hot-middleware-client/client.js +6 -0
  128. package/dist/state/editor-outlines.d.ts +8 -0
  129. package/dist/state/editor-outlines.js +18 -0
  130. package/dist/state/modals.d.ts +19 -2
  131. package/package.json +10 -10
  132. package/dist/helpers/detect-file-type.d.ts +0 -69
  133. package/dist/helpers/detect-file-type.js +0 -278
@@ -3,28 +3,46 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.saveEffectProp = void 0;
4
4
  const studio_shared_1 = require("@remotion/studio-shared");
5
5
  const call_api_1 = require("../call-api");
6
+ const apply_effect_response_to_code_values_1 = require("./apply-effect-response-to-code-values");
6
7
  const save_prop_queue_1 = require("./save-prop-queue");
7
- const saveEffectProp = ({ fileName, nodePath, effectIndex, fieldKey, value, defaultValue, schema, setCodeValues, clientId, }) => {
8
+ const saveEffectProp = (input) => {
9
+ const { fileName, nodePath, effectIndex, fieldKey, defaultValue, schema, setCodeValues, clientId, } = input;
8
10
  return (0, save_prop_queue_1.enqueueSavePropChange)({
9
11
  nodePath,
10
12
  setCodeValues,
11
- applyOptimistic: (prev) => (0, studio_shared_1.optimisticUpdateForEffectCodeValues)({
12
- previous: prev,
13
- effectIndex,
14
- fieldKey,
15
- value,
16
- schema,
17
- }),
18
- apiCall: () => (0, call_api_1.callApi)('/api/save-effect-props', {
19
- fileName,
20
- sequenceNodePath: nodePath,
21
- effectIndex,
22
- key: fieldKey,
23
- value: JSON.stringify(value),
24
- defaultValue,
25
- schema,
26
- clientId,
27
- }),
13
+ applyOptimistic: (prev) => input.type === 'effect-param'
14
+ ? prev
15
+ : (0, studio_shared_1.optimisticUpdateForEffectCodeValues)({
16
+ previous: prev,
17
+ effectIndex,
18
+ fieldKey,
19
+ value: input.value,
20
+ schema,
21
+ }),
22
+ applyServerResponse: (prev, response) => (0, apply_effect_response_to_code_values_1.applyEffectResponseToCodeValues)({ previous: prev, response }),
23
+ apiCall: () => (0, call_api_1.callApi)('/api/save-effect-props', input.type === 'effect-param'
24
+ ? {
25
+ type: 'effect-param',
26
+ fileName,
27
+ sequenceNodePath: nodePath,
28
+ effectIndex,
29
+ key: fieldKey,
30
+ effectParam: input.effectParam,
31
+ defaultValue,
32
+ schema,
33
+ clientId,
34
+ }
35
+ : {
36
+ type: 'value',
37
+ fileName,
38
+ sequenceNodePath: nodePath,
39
+ effectIndex,
40
+ key: fieldKey,
41
+ value: JSON.stringify(input.value),
42
+ defaultValue,
43
+ schema,
44
+ clientId,
45
+ }),
28
46
  errorLabel: 'Could not save effect prop',
29
47
  });
30
48
  };
@@ -4,8 +4,9 @@ export type EnqueueSaveOptions<TResponse> = {
4
4
  nodePath: SequencePropsSubscriptionKey;
5
5
  setCodeValues: SetCodeValues;
6
6
  applyOptimistic: (prev: CanUpdateSequencePropsResponse) => CanUpdateSequencePropsResponse;
7
+ applyServerResponse?: (prev: CanUpdateSequencePropsResponse, response: TResponse) => CanUpdateSequencePropsResponse;
7
8
  apiCall: () => Promise<TResponse>;
8
9
  errorLabel: string;
9
10
  };
10
- export declare const enqueueSavePropChange: <TResponse>({ nodePath, setCodeValues, applyOptimistic, apiCall, errorLabel, }: EnqueueSaveOptions<TResponse>) => Promise<void>;
11
+ export declare const enqueueSavePropChange: <TResponse>({ nodePath, setCodeValues, applyOptimistic, applyServerResponse, apiCall, errorLabel, }: EnqueueSaveOptions<TResponse>) => Promise<void>;
11
12
  export {};
@@ -19,7 +19,7 @@ const dropQueue = (nodePath, q) => {
19
19
  queues.delete(key);
20
20
  }
21
21
  };
22
- const enqueueSavePropChange = ({ nodePath, setCodeValues, applyOptimistic, apiCall, errorLabel, }) => {
22
+ const enqueueSavePropChange = ({ nodePath, setCodeValues, applyOptimistic, applyServerResponse, apiCall, errorLabel, }) => {
23
23
  const q = getQueue(nodePath);
24
24
  if (q.cancelled) {
25
25
  return Promise.resolve();
@@ -33,12 +33,15 @@ const enqueueSavePropChange = ({ nodePath, setCodeValues, applyOptimistic, apiCa
33
33
  return;
34
34
  }
35
35
  try {
36
- await apiCall();
36
+ const response = await apiCall();
37
37
  if (myQueue.cancelled) {
38
38
  return;
39
39
  }
40
40
  // If nothing more is queued, reset baseline so the next round starts fresh.
41
41
  if (myQueue.chain === next) {
42
+ if (applyServerResponse) {
43
+ setCodeValues(nodePath, (prev) => applyServerResponse(prev, response));
44
+ }
42
45
  dropQueue(nodePath, myQueue);
43
46
  }
44
47
  }
@@ -12,13 +12,8 @@ type SaveSequencePropsOptions = {
12
12
  changes: SaveSequencePropChange[];
13
13
  setCodeValues: SetCodeValues;
14
14
  clientId: string;
15
- undoLabel: string | null;
16
- redoLabel: string | null;
17
- };
18
- type SaveSequencePropOptions = SaveSequencePropChange & {
19
- setCodeValues: SetCodeValues;
20
- clientId: string;
15
+ undoLabel: string;
16
+ redoLabel: string;
21
17
  };
22
18
  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
19
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.saveSequenceProp = exports.saveSequenceProps = void 0;
3
+ 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");
@@ -8,6 +8,38 @@ const saveSequenceProps = ({ changes, setCodeValues, clientId, undoLabel, redoLa
8
8
  if (changes.length === 0) {
9
9
  return Promise.resolve();
10
10
  }
11
+ if (changes.length === 1) {
12
+ const change = changes[0];
13
+ if (change === undefined) {
14
+ throw new Error('Expected a sequence prop change');
15
+ }
16
+ return (0, save_prop_queue_1.enqueueSavePropChange)({
17
+ nodePath: change.nodePath,
18
+ setCodeValues,
19
+ applyOptimistic: (prev) => (0, studio_shared_1.optimisticUpdateForCodeValues)({
20
+ previous: prev,
21
+ fieldKey: change.fieldKey,
22
+ value: change.value,
23
+ schema: change.schema,
24
+ }),
25
+ apiCall: () => (0, call_api_1.callApi)('/api/save-sequence-props', {
26
+ edits: [
27
+ {
28
+ fileName: change.fileName,
29
+ nodePath: change.nodePath,
30
+ key: change.fieldKey,
31
+ value: JSON.stringify(change.value),
32
+ defaultValue: change.defaultValue,
33
+ schema: change.schema,
34
+ },
35
+ ],
36
+ clientId,
37
+ undoLabel,
38
+ redoLabel,
39
+ }),
40
+ errorLabel: 'Could not save sequence prop',
41
+ });
42
+ }
11
43
  for (const change of changes) {
12
44
  setCodeValues(change.nodePath, (prev) => (0, studio_shared_1.optimisticUpdateForCodeValues)({
13
45
  previous: prev,
@@ -33,32 +65,3 @@ const saveSequenceProps = ({ changes, setCodeValues, clientId, undoLabel, redoLa
33
65
  }).then(() => undefined);
34
66
  };
35
67
  exports.saveSequenceProps = saveSequenceProps;
36
- const saveSequenceProp = ({ fileName, nodePath, fieldKey, value, defaultValue, schema, setCodeValues, clientId, }) => {
37
- return (0, save_prop_queue_1.enqueueSavePropChange)({
38
- nodePath,
39
- setCodeValues,
40
- applyOptimistic: (prev) => (0, studio_shared_1.optimisticUpdateForCodeValues)({
41
- previous: prev,
42
- fieldKey,
43
- value,
44
- schema,
45
- }),
46
- apiCall: () => (0, call_api_1.callApi)('/api/save-sequence-props', {
47
- edits: [
48
- {
49
- fileName,
50
- nodePath,
51
- key: fieldKey,
52
- value: JSON.stringify(value),
53
- defaultValue,
54
- schema,
55
- },
56
- ],
57
- clientId,
58
- undoLabel: null,
59
- redoLabel: null,
60
- }),
61
- errorLabel: 'Could not save sequence prop',
62
- });
63
- };
64
- exports.saveSequenceProp = saveSequenceProp;
@@ -0,0 +1,3 @@
1
+ export declare const shouldClearSelectionOnPointerDown: (event: {
2
+ readonly button: number;
3
+ }) => boolean;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.shouldClearSelectionOnPointerDown = void 0;
4
+ const shouldClearSelectionOnPointerDown = (event) => {
5
+ return event.button === 0;
6
+ };
7
+ exports.shouldClearSelectionOnPointerDown = shouldClearSelectionOnPointerDown;
@@ -0,0 +1,13 @@
1
+ type LinkInfo = {
2
+ kind: 'local';
3
+ assetPath: string;
4
+ title: string;
5
+ } | {
6
+ kind: 'remote';
7
+ href: string;
8
+ title: string;
9
+ } | null;
10
+ export type TimelineAssetLinkInfo = Exclude<LinkInfo, null>;
11
+ export declare const getTimelineAssetLinkInfo: (src: string) => LinkInfo;
12
+ export declare const openTimelineAssetLink: (linkInfo: TimelineAssetLinkInfo, selectAsset: (asset: string) => void) => void;
13
+ export {};
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.openTimelineAssetLink = exports.getTimelineAssetLinkInfo = void 0;
4
+ const url_state_1 = require("../../helpers/url-state");
5
+ const getTimelineAssetLinkInfo = (src) => {
6
+ const staticBase = typeof window === 'undefined' ? null : window.remotion_staticBase;
7
+ if (staticBase && src.startsWith(staticBase + '/')) {
8
+ const assetPath = src.slice(staticBase.length + 1);
9
+ return {
10
+ kind: 'local',
11
+ assetPath: decodeURIComponent(assetPath),
12
+ title: decodeURIComponent(assetPath),
13
+ };
14
+ }
15
+ if (src.startsWith('http://') ||
16
+ src.startsWith('https://') ||
17
+ src.startsWith('//')) {
18
+ try {
19
+ const url = new URL(src.startsWith('//') ? 'https:' + src : src);
20
+ return { kind: 'remote', href: src, title: url.hostname };
21
+ }
22
+ catch (_a) {
23
+ return { kind: 'remote', href: src, title: src };
24
+ }
25
+ }
26
+ return null;
27
+ };
28
+ exports.getTimelineAssetLinkInfo = getTimelineAssetLinkInfo;
29
+ const openTimelineAssetLink = (linkInfo, selectAsset) => {
30
+ if (linkInfo.kind === 'local') {
31
+ selectAsset(linkInfo.assetPath);
32
+ (0, url_state_1.pushUrl)(`/assets/${linkInfo.assetPath}`);
33
+ return;
34
+ }
35
+ window.open(linkInfo.href, '_blank', 'noopener,noreferrer');
36
+ };
37
+ exports.openTimelineAssetLink = openTimelineAssetLink;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.serializeTranslate = exports.parseTranslate = void 0;
4
4
  const timeline_field_utils_1 = require("./timeline-field-utils");
5
5
  const PIXEL_PATTERN = /^(-?\d+(?:\.\d+)?)px(?:\s+(-?\d+(?:\.\d+)?)px)?$/;
6
+ const translateDecimalPlaces = 1;
6
7
  const parseTranslate = (value) => {
7
8
  const m = value.match(PIXEL_PATTERN);
8
9
  if (!m) {
@@ -15,7 +16,9 @@ const parseTranslate = (value) => {
15
16
  };
16
17
  exports.parseTranslate = parseTranslate;
17
18
  const formatTranslateCoordinate = (value) => {
18
- const rounded = (0, timeline_field_utils_1.normalizeTimelineNumber)(value);
19
+ const normalized = (0, timeline_field_utils_1.normalizeTimelineNumber)(value);
20
+ const factor = 10 ** translateDecimalPlaces;
21
+ const rounded = Math.round(normalized * factor) / factor;
19
22
  return String(Object.is(rounded, -0) ? 0 : rounded);
20
23
  };
21
24
  const serializeTranslate = (x, y) => {
@@ -0,0 +1,10 @@
1
+ import type React from 'react';
2
+ import type { SequenceNodePathInfo } from '../../helpers/get-timeline-sequence-sort-key';
3
+ import { type TimelineSelectionInteraction } from './TimelineSelection';
4
+ export declare const useTimelineKeyframeDrag: ({ frame, nodePathInfo, onSelect, selectable, selected, }: {
5
+ readonly frame: number;
6
+ readonly nodePathInfo: SequenceNodePathInfo;
7
+ readonly onSelect: (interaction?: TimelineSelectionInteraction | undefined) => void;
8
+ readonly selectable: boolean;
9
+ readonly selected: boolean;
10
+ }) => (e: React.PointerEvent<HTMLButtonElement>) => void;
@@ -0,0 +1,378 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useTimelineKeyframeDrag = void 0;
4
+ const studio_shared_1 = require("@remotion/studio-shared");
5
+ const react_1 = require("react");
6
+ const remotion_1 = require("remotion");
7
+ const client_id_1 = require("../../helpers/client-id");
8
+ const timeline_layout_1 = require("../../helpers/timeline-layout");
9
+ const call_move_keyframe_1 = require("./call-move-keyframe");
10
+ const find_track_for_node_path_info_1 = require("./find-track-for-node-path-info");
11
+ const get_bounded_keyframe_drag_delta_1 = require("./get-bounded-keyframe-drag-delta");
12
+ const parse_keyframe_field_from_node_path_1 = require("./parse-keyframe-field-from-node-path");
13
+ const TimelineKeyframeDragState_1 = require("./TimelineKeyframeDragState");
14
+ const TimelineSelection_1 = require("./TimelineSelection");
15
+ const TimelineWidthProvider_1 = require("./TimelineWidthProvider");
16
+ const pointerDragThreshold = 3;
17
+ const isKeyframeSelection = (selection) => selection.type === 'keyframe';
18
+ const getCodeValueForTarget = ({ codeValues, nodePath, }) => codeValues[remotion_1.Internals.makeSequencePropsSubscriptionKey(nodePath)];
19
+ const getTimelineKeyframeDragTarget = ({ codeValues, displayFrame, nodePathInfo, overrideIdsToNodePaths, sequences, }) => {
20
+ var _a, _b;
21
+ const field = (0, parse_keyframe_field_from_node_path_1.parseKeyframeFieldFromNodePath)(nodePathInfo.auxiliaryKeys);
22
+ if (field === null) {
23
+ return null;
24
+ }
25
+ const track = (0, find_track_for_node_path_info_1.findTrackForNodePathInfo)({
26
+ sequences,
27
+ overrideIdsToNodePaths,
28
+ nodePathInfo,
29
+ });
30
+ const sequence = (_a = track === null || track === void 0 ? void 0 : track.sequence) !== null && _a !== void 0 ? _a : null;
31
+ if (!(sequence === null || sequence === void 0 ? void 0 : sequence.controls)) {
32
+ return null;
33
+ }
34
+ const sourceFrame = displayFrame - ((_b = track === null || track === void 0 ? void 0 : track.keyframeDisplayOffset) !== null && _b !== void 0 ? _b : 0);
35
+ const nodePath = nodePathInfo.sequenceSubscriptionKey;
36
+ const fileName = nodePath.absolutePath;
37
+ const sequenceStatus = getCodeValueForTarget({ codeValues, nodePath });
38
+ if (!(sequenceStatus === null || sequenceStatus === void 0 ? void 0 : sequenceStatus.canUpdate)) {
39
+ return null;
40
+ }
41
+ if (field.type === 'effect') {
42
+ const effect = sequence.effects[field.effectIndex];
43
+ const effectStatus = sequenceStatus.effects.find((candidate) => candidate.effectIndex === field.effectIndex);
44
+ if (!effect || !(effectStatus === null || effectStatus === void 0 ? void 0 : effectStatus.canUpdate)) {
45
+ return null;
46
+ }
47
+ const effectPropStatus = effectStatus.props[field.fieldKey];
48
+ if ((effectPropStatus === null || effectPropStatus === void 0 ? void 0 : effectPropStatus.status) !== 'keyframed') {
49
+ return null;
50
+ }
51
+ if (!effectPropStatus.keyframes.some((keyframe) => keyframe.frame === sourceFrame)) {
52
+ return null;
53
+ }
54
+ return {
55
+ type: 'effect',
56
+ displayFrame,
57
+ effectIndex: field.effectIndex,
58
+ fieldKey: field.fieldKey,
59
+ fileName,
60
+ nodePath,
61
+ nodePathInfo,
62
+ propStatus: effectPropStatus,
63
+ schema: effect.schema,
64
+ sourceFrame,
65
+ };
66
+ }
67
+ const sequencePropStatus = sequenceStatus.props[field.fieldKey];
68
+ if ((sequencePropStatus === null || sequencePropStatus === void 0 ? void 0 : sequencePropStatus.status) !== 'keyframed') {
69
+ return null;
70
+ }
71
+ if (!sequencePropStatus.keyframes.some((keyframe) => keyframe.frame === sourceFrame)) {
72
+ return null;
73
+ }
74
+ return {
75
+ type: 'sequence',
76
+ displayFrame,
77
+ fieldKey: field.fieldKey,
78
+ fileName,
79
+ nodePath,
80
+ nodePathInfo,
81
+ propStatus: sequencePropStatus,
82
+ schema: sequence.controls.schema,
83
+ sourceFrame,
84
+ };
85
+ };
86
+ const getTargetGroupKey = (target) => {
87
+ return JSON.stringify({
88
+ type: target.type,
89
+ nodePath: target.nodePath,
90
+ effectIndex: target.type === 'effect' ? target.effectIndex : null,
91
+ fieldKey: target.fieldKey,
92
+ });
93
+ };
94
+ const groupTargets = (targets) => {
95
+ var _a;
96
+ const groups = new Map();
97
+ for (const target of targets) {
98
+ const key = getTargetGroupKey(target);
99
+ const group = (_a = groups.get(key)) !== null && _a !== void 0 ? _a : [];
100
+ group.push(target);
101
+ groups.set(key, group);
102
+ }
103
+ return [...groups.values()];
104
+ };
105
+ const getMovesForGroup = ({ group, delta, }) => group.map((target) => ({
106
+ fromFrame: target.sourceFrame,
107
+ toFrame: target.sourceFrame + delta,
108
+ }));
109
+ const canMoveTimelineKeyframeDragTargets = ({ targets, delta, }) => groupTargets(targets).every((group) => {
110
+ const [first] = group;
111
+ if (!first) {
112
+ return true;
113
+ }
114
+ return (0, studio_shared_1.canMoveKeyframesWithoutCollisions)({
115
+ status: first.propStatus,
116
+ moves: getMovesForGroup({ group, delta }),
117
+ });
118
+ });
119
+ const makeMovedKeyframedDragOverride = ({ group, delta, }) => {
120
+ const [first] = group;
121
+ if (!first) {
122
+ throw new Error('Expected a keyframe drag target');
123
+ }
124
+ const moves = new Map(getMovesForGroup({ group, delta }).map((move) => [move.fromFrame, move.toFrame]));
125
+ return {
126
+ type: 'keyframed',
127
+ status: {
128
+ ...first.propStatus,
129
+ keyframes: first.propStatus.keyframes
130
+ .map((keyframe) => {
131
+ var _a;
132
+ return ({
133
+ ...keyframe,
134
+ frame: (_a = moves.get(keyframe.frame)) !== null && _a !== void 0 ? _a : keyframe.frame,
135
+ });
136
+ })
137
+ .sort((a, b) => a.frame - b.frame),
138
+ },
139
+ };
140
+ };
141
+ const applyDragOverrides = ({ delta, setDragOverrides, setEffectDragOverrides, targets, }) => {
142
+ for (const group of groupTargets(targets)) {
143
+ const [first] = group;
144
+ if (!first) {
145
+ continue;
146
+ }
147
+ const override = makeMovedKeyframedDragOverride({ group, delta });
148
+ if (first.type === 'sequence') {
149
+ setDragOverrides(first.nodePath, first.fieldKey, override);
150
+ continue;
151
+ }
152
+ setEffectDragOverrides(first.nodePath, first.effectIndex, first.fieldKey, override);
153
+ }
154
+ };
155
+ const clearDragOverridesForTargets = ({ clearDragOverrides, clearEffectDragOverrides, targets, }) => {
156
+ const clearedSequences = new Set();
157
+ const clearedEffects = new Set();
158
+ for (const target of targets) {
159
+ if (target.type === 'sequence') {
160
+ const sequenceKey = JSON.stringify(target.nodePath);
161
+ if (clearedSequences.has(sequenceKey)) {
162
+ continue;
163
+ }
164
+ clearedSequences.add(sequenceKey);
165
+ clearDragOverrides(target.nodePath);
166
+ continue;
167
+ }
168
+ const effectKey = JSON.stringify({
169
+ nodePath: target.nodePath,
170
+ effectIndex: target.effectIndex,
171
+ });
172
+ if (clearedEffects.has(effectKey)) {
173
+ continue;
174
+ }
175
+ clearedEffects.add(effectKey);
176
+ clearEffectDragOverrides(target.nodePath, target.effectIndex);
177
+ }
178
+ };
179
+ const getFrameDelta = ({ clientXDelta, durationInFrames, timelineWidth, }) => {
180
+ const timelineContentWidth = timelineWidth - timeline_layout_1.TIMELINE_PADDING * 2;
181
+ if (timelineContentWidth <= 0) {
182
+ return 0;
183
+ }
184
+ return Math.round((clientXDelta / timelineContentWidth) * durationInFrames);
185
+ };
186
+ const useTimelineKeyframeDrag = ({ frame, nodePathInfo, onSelect, selectable, selected, }) => {
187
+ const videoConfig = (0, remotion_1.useVideoConfig)();
188
+ const timelineWidth = (0, react_1.useContext)(TimelineWidthProvider_1.TimelineWidthContext);
189
+ const { previewServerState } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
190
+ const { sequences } = (0, react_1.useContext)(remotion_1.Internals.SequenceManager);
191
+ const { overrideIdToNodePathMappings } = (0, react_1.useContext)(remotion_1.Internals.OverrideIdsToNodePathsGettersContext);
192
+ const { codeValues } = (0, react_1.useContext)(remotion_1.Internals.VisualModeCodeValuesContext);
193
+ const { clearDragOverrides, clearEffectDragOverrides, setCodeValues, setDragOverrides, setEffectDragOverrides, } = (0, react_1.useContext)(remotion_1.Internals.VisualModeSettersContext);
194
+ const currentSelection = (0, TimelineSelection_1.useCurrentTimelineSelectionStateAsRef)();
195
+ const { clearDraggedKeyframes, setDraggedKeyframes } = (0, TimelineKeyframeDragState_1.useTimelineKeyframeDragState)();
196
+ return (0, react_1.useCallback)((e) => {
197
+ if (e.button !== 0 ||
198
+ !selectable ||
199
+ timelineWidth === null ||
200
+ previewServerState.type !== 'connected') {
201
+ return;
202
+ }
203
+ e.preventDefault();
204
+ e.stopPropagation();
205
+ const interaction = {
206
+ shiftKey: e.shiftKey,
207
+ toggleKey: e.metaKey || e.ctrlKey,
208
+ };
209
+ const shouldDragExistingSelection = selected && !interaction.shiftKey && !interaction.toggleKey;
210
+ if (!shouldDragExistingSelection) {
211
+ onSelect(interaction);
212
+ }
213
+ const clickedSelection = {
214
+ type: 'keyframe',
215
+ nodePathInfo,
216
+ frame,
217
+ };
218
+ const selectedKeyframes = currentSelection.current.selectedItems.filter(isKeyframeSelection);
219
+ const keyframesToDrag = shouldDragExistingSelection && selectedKeyframes.length > 0
220
+ ? selectedKeyframes
221
+ : [clickedSelection];
222
+ const startClientX = e.clientX;
223
+ let dragTargets = null;
224
+ let hasDragged = false;
225
+ let lastDelta = 0;
226
+ const resolveDragTargets = () => {
227
+ if (dragTargets !== null) {
228
+ return dragTargets;
229
+ }
230
+ dragTargets = keyframesToDrag
231
+ .map((keyframe) => getTimelineKeyframeDragTarget({
232
+ codeValues,
233
+ displayFrame: keyframe.frame,
234
+ nodePathInfo: keyframe.nodePathInfo,
235
+ overrideIdsToNodePaths: overrideIdToNodePathMappings,
236
+ sequences,
237
+ }))
238
+ .filter((target) => target !== null);
239
+ return dragTargets;
240
+ };
241
+ const cleanup = () => {
242
+ window.removeEventListener('pointermove', onPointerMove);
243
+ window.removeEventListener('pointerup', onPointerUp);
244
+ window.removeEventListener('pointercancel', onPointerCancel);
245
+ };
246
+ const clearActiveOverrides = () => {
247
+ const targets = dragTargets;
248
+ if (targets === null) {
249
+ return;
250
+ }
251
+ clearDragOverridesForTargets({
252
+ clearDragOverrides,
253
+ clearEffectDragOverrides,
254
+ targets,
255
+ });
256
+ };
257
+ const onPointerMove = (event) => {
258
+ const clientXDelta = event.clientX - startClientX;
259
+ if (!hasDragged && Math.abs(clientXDelta) < pointerDragThreshold) {
260
+ return;
261
+ }
262
+ const targets = resolveDragTargets();
263
+ if (targets.length === 0) {
264
+ cleanup();
265
+ clearDraggedKeyframes();
266
+ return;
267
+ }
268
+ const rawDelta = getFrameDelta({
269
+ clientXDelta,
270
+ durationInFrames: videoConfig.durationInFrames,
271
+ timelineWidth,
272
+ });
273
+ const delta = (0, get_bounded_keyframe_drag_delta_1.getBoundedKeyframeDragDelta)({
274
+ delta: rawDelta,
275
+ durationInFrames: videoConfig.durationInFrames,
276
+ targets,
277
+ });
278
+ if (hasDragged && delta === lastDelta) {
279
+ return;
280
+ }
281
+ hasDragged = true;
282
+ lastDelta = delta;
283
+ if (!canMoveTimelineKeyframeDragTargets({ targets, delta })) {
284
+ clearActiveOverrides();
285
+ clearDraggedKeyframes();
286
+ return;
287
+ }
288
+ setDraggedKeyframes(targets.map((target) => ({
289
+ nodePathInfo: target.nodePathInfo,
290
+ frame: target.displayFrame + delta,
291
+ })));
292
+ applyDragOverrides({
293
+ delta,
294
+ setDragOverrides,
295
+ setEffectDragOverrides,
296
+ targets,
297
+ });
298
+ };
299
+ const onPointerUp = () => {
300
+ cleanup();
301
+ const targets = dragTargets;
302
+ if (!hasDragged || lastDelta === 0 || targets === null) {
303
+ clearActiveOverrides();
304
+ clearDraggedKeyframes();
305
+ return;
306
+ }
307
+ if (!canMoveTimelineKeyframeDragTargets({
308
+ targets,
309
+ delta: lastDelta,
310
+ })) {
311
+ clearActiveOverrides();
312
+ clearDraggedKeyframes();
313
+ return;
314
+ }
315
+ currentSelection.current.selectItems(targets.map((target) => ({
316
+ type: 'keyframe',
317
+ nodePathInfo: target.nodePathInfo,
318
+ frame: target.displayFrame + lastDelta,
319
+ })));
320
+ clearActiveOverrides();
321
+ clearDraggedKeyframes();
322
+ (0, call_move_keyframe_1.callMoveKeyframes)({
323
+ sequenceKeyframes: targets
324
+ .filter((target) => target.type === 'sequence')
325
+ .map((target) => ({
326
+ fileName: target.fileName,
327
+ nodePath: target.nodePath,
328
+ fieldKey: target.fieldKey,
329
+ fromFrame: target.sourceFrame,
330
+ toFrame: target.sourceFrame + lastDelta,
331
+ schema: target.schema,
332
+ })),
333
+ effectKeyframes: targets
334
+ .filter((target) => target.type === 'effect')
335
+ .map((target) => ({
336
+ fileName: target.fileName,
337
+ nodePath: target.nodePath,
338
+ effectIndex: target.effectIndex,
339
+ fieldKey: target.fieldKey,
340
+ fromFrame: target.sourceFrame,
341
+ toFrame: target.sourceFrame + lastDelta,
342
+ schema: target.schema,
343
+ })),
344
+ setCodeValues,
345
+ clientId: previewServerState.clientId,
346
+ }).catch(() => undefined);
347
+ };
348
+ const onPointerCancel = () => {
349
+ cleanup();
350
+ clearActiveOverrides();
351
+ clearDraggedKeyframes();
352
+ };
353
+ window.addEventListener('pointermove', onPointerMove);
354
+ window.addEventListener('pointerup', onPointerUp);
355
+ window.addEventListener('pointercancel', onPointerCancel);
356
+ }, [
357
+ clearDragOverrides,
358
+ clearEffectDragOverrides,
359
+ clearDraggedKeyframes,
360
+ codeValues,
361
+ currentSelection,
362
+ frame,
363
+ nodePathInfo,
364
+ onSelect,
365
+ overrideIdToNodePathMappings,
366
+ previewServerState,
367
+ selectable,
368
+ selected,
369
+ sequences,
370
+ setCodeValues,
371
+ setDragOverrides,
372
+ setDraggedKeyframes,
373
+ setEffectDragOverrides,
374
+ timelineWidth,
375
+ videoConfig.durationInFrames,
376
+ ]);
377
+ };
378
+ exports.useTimelineKeyframeDrag = useTimelineKeyframeDrag;