@remotion/studio 4.0.508 → 4.0.510

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 (117) hide show
  1. package/dist/Studio.d.ts +5 -0
  2. package/dist/Studio.js +2 -2
  3. package/dist/components/Canvas.js +41 -8
  4. package/dist/components/ColorPicker/AlphaSlider.js +2 -2
  5. package/dist/components/ColorPicker/HueSlider.js +2 -2
  6. package/dist/components/ColorPicker/SaturationValueArea.js +2 -2
  7. package/dist/components/CompositionSelectorItem.js +1 -1
  8. package/dist/components/EditorGuides/Guide.js +2 -2
  9. package/dist/components/EditorRuler/index.js +2 -2
  10. package/dist/components/ExplorerPanel.js +19 -1
  11. package/dist/components/InspectorOpenInEditor.js +19 -5
  12. package/dist/components/InspectorPanel/CompositionMetadata.js +23 -1
  13. package/dist/components/InspectorPanel/SequenceSelectionInspector.js +3 -3
  14. package/dist/components/Menu/MenuItem.js +2 -2
  15. package/dist/components/ModalContainer.js +1 -1
  16. package/dist/components/Modals.js +2 -9
  17. package/dist/components/NewComposition/InputDragger.js +2 -2
  18. package/dist/components/NewComposition/NewComposition.d.ts +2 -0
  19. package/dist/components/NewComposition/NewComposition.js +38 -6
  20. package/dist/components/NewComposition/get-new-composition-defaults.d.ts +1 -1
  21. package/dist/components/NewComposition/get-new-composition-defaults.js +9 -1
  22. package/dist/components/QuickSwitcher/asset-search.js +1 -0
  23. package/dist/components/SelectedOutlineCanvasRotation.js +6 -4
  24. package/dist/components/SelectedOutlineCropControls.js +3 -3
  25. package/dist/components/SelectedOutlineElement.js +6 -2
  26. package/dist/components/SelectedOutlinePolygon.js +4 -3
  27. package/dist/components/SelectedOutlineRotationCornerHandle.d.ts +5 -0
  28. package/dist/components/SelectedOutlineRotationCornerHandle.js +4 -4
  29. package/dist/components/SelectedOutlineScaleEdgeLine.d.ts +1 -0
  30. package/dist/components/SelectedOutlineScaleEdgeLine.js +4 -4
  31. package/dist/components/SelectedOutlineTransformOriginHandle.js +2 -2
  32. package/dist/components/SelectedOutlineUvControls.js +8 -8
  33. package/dist/components/Splitter/SplitterHandle.js +2 -2
  34. package/dist/components/Tabs/index.d.ts +2 -0
  35. package/dist/components/Tabs/index.js +2 -2
  36. package/dist/components/Timeline/EasingEditorModal.d.ts +8 -0
  37. package/dist/components/Timeline/EasingEditorModal.js +10 -11
  38. package/dist/components/Timeline/SubscribeToNodePaths.js +1 -1
  39. package/dist/components/Timeline/Timeline.js +55 -1
  40. package/dist/components/Timeline/TimelineClipboardKeybindings.js +99 -35
  41. package/dist/components/Timeline/TimelineColorField.js +1 -6
  42. package/dist/components/Timeline/TimelineDragHandler.js +2 -2
  43. package/dist/components/Timeline/TimelineEffectItem.js +1 -1
  44. package/dist/components/Timeline/TimelineEffectPropItem.js +8 -1
  45. package/dist/components/Timeline/TimelineEnumField.js +1 -4
  46. package/dist/components/Timeline/TimelineExpandedRow.js +2 -2
  47. package/dist/components/Timeline/TimelineFieldRowContent.d.ts +2 -1
  48. package/dist/components/Timeline/TimelineFieldRowContent.js +18 -3
  49. package/dist/components/Timeline/TimelineFontFamilyField.js +1 -2
  50. package/dist/components/Timeline/TimelineInOutDragHandler.js +2 -2
  51. package/dist/components/Timeline/TimelineKeyframeEasingLine.js +3 -2
  52. package/dist/components/Timeline/TimelineLayerEye.js +2 -2
  53. package/dist/components/Timeline/TimelineNumberField.js +4 -2
  54. package/dist/components/Timeline/TimelineRotationField.js +3 -3
  55. package/dist/components/Timeline/TimelineRowChrome.d.ts +5 -0
  56. package/dist/components/Timeline/TimelineRowChrome.js +14 -9
  57. package/dist/components/Timeline/TimelineRowLayoutContext.d.ts +1 -0
  58. package/dist/components/Timeline/TimelineRowLayoutContext.js +4 -1
  59. package/dist/components/Timeline/TimelineScaleField.js +4 -0
  60. package/dist/components/Timeline/TimelineSelection.js +2 -2
  61. package/dist/components/Timeline/TimelineSequenceItem.js +2 -25
  62. package/dist/components/Timeline/TimelineSequencePropItem.js +8 -2
  63. package/dist/components/Timeline/TimelineSequenceRightEdgeDragHandle.d.ts +2 -0
  64. package/dist/components/Timeline/TimelineSequenceRightEdgeDragHandle.js +69 -20
  65. package/dist/components/Timeline/TimelineTextContentField.js +1 -1
  66. package/dist/components/Timeline/get-sequence-context-menu-items.js +2 -0
  67. package/dist/components/Timeline/keyframe-clipboard.d.ts +5 -1
  68. package/dist/components/Timeline/keyframe-clipboard.js +3 -2
  69. package/dist/components/Timeline/save-sequence-prop.js +16 -7
  70. package/dist/components/Timeline/sequence-prop-clipboard.d.ts +31 -0
  71. package/dist/components/Timeline/sequence-prop-clipboard.js +142 -0
  72. package/dist/components/Timeline/split-selected-timeline-item.d.ts +0 -1
  73. package/dist/components/Timeline/split-selected-timeline-item.js +5 -13
  74. package/dist/components/Timeline/timeline-field-row-layout.js +2 -2
  75. package/dist/components/Timeline/timeline-field-utils.d.ts +1 -0
  76. package/dist/components/Timeline/timeline-field-utils.js +6 -1
  77. package/dist/components/Timeline/update-selected-easing.js +8 -0
  78. package/dist/components/Timeline/use-open-sequence-in-apps.js +3 -1
  79. package/dist/components/Timeline/use-resolved-stack-react-to-change.d.ts +1 -1
  80. package/dist/components/Timeline/use-resolved-stack-react-to-change.js +24 -3
  81. package/dist/components/Timeline/use-timeline-keyframe-drag.js +4 -4
  82. package/dist/components/UndoRedoButtons.js +5 -3
  83. package/dist/components/folder-menu-items.js +1 -0
  84. package/dist/components/get-open-in-menu-items.d.ts +4 -1
  85. package/dist/components/get-open-in-menu-items.js +99 -50
  86. package/dist/components/import-assets.js +7 -0
  87. package/dist/components/root-composition-menu-items.js +1 -0
  88. package/dist/components/selected-outline-control-layout.d.ts +14 -0
  89. package/dist/components/selected-outline-control-layout.js +94 -0
  90. package/dist/components/use-default-editor-info.js +28 -3
  91. package/dist/esm/{chunk-zpn4m49r.js → chunk-axbagh08.js} +6098 -5285
  92. package/dist/esm/{chunk-fgedsvhb.js → chunk-r46yxrjr.js} +1 -2
  93. package/dist/esm/internals.mjs +6097 -5284
  94. package/dist/esm/previewEntry.mjs +5989 -5176
  95. package/dist/esm/renderEntry.mjs +4 -2
  96. package/dist/helpers/get-canvas-capture-import.d.ts +7 -0
  97. package/dist/helpers/get-canvas-capture-import.js +43 -0
  98. package/dist/helpers/get-git-menu-item.d.ts +1 -1
  99. package/dist/helpers/get-git-menu-item.js +33 -3
  100. package/dist/helpers/inserted-element-selection.d.ts +12 -0
  101. package/dist/helpers/inserted-element-selection.js +29 -0
  102. package/dist/helpers/open-in-editor.d.ts +1 -0
  103. package/dist/helpers/open-in-editor.js +5 -1
  104. package/dist/helpers/pointer-session.d.ts +6 -2
  105. package/dist/helpers/pointer-session.js +29 -12
  106. package/dist/helpers/use-create-composition.d.ts +15 -2
  107. package/dist/helpers/use-create-composition.js +11 -17
  108. package/dist/helpers/use-menu-structure.js +1 -0
  109. package/dist/icons/git-client.d.ts +6 -0
  110. package/dist/icons/git-client.js +8 -0
  111. package/dist/icons/github.d.ts +4 -0
  112. package/dist/icons/github.js +11 -0
  113. package/dist/renderEntry.js +2 -2
  114. package/dist/state/modals.d.ts +9 -1
  115. package/dist/state/z-index.d.ts +0 -1
  116. package/dist/state/z-index.js +27 -33
  117. package/package.json +12 -12
@@ -4,7 +4,7 @@ import {
4
4
  WHITE,
5
5
  __require,
6
6
  __toESM
7
- } from "./chunk-fgedsvhb.js";
7
+ } from "./chunk-r46yxrjr.js";
8
8
 
9
9
  // src/renderEntry.tsx
10
10
  import { useContext, useEffect, useRef, useState } from "react";
@@ -170,6 +170,7 @@ var renderContent = (Root) => {
170
170
  numberOfAudioTags: 0,
171
171
  audioLatencyHint: window.remotion_audioLatencyHint ?? "playback",
172
172
  previewSampleRate: window.remotion_previewSampleRate,
173
+ _experimentalKeepAudioContextAlive: false,
173
174
  children: /* @__PURE__ */ jsxs(Internals.RenderAssetManagerProvider, {
174
175
  collectAssets: null,
175
176
  children: [
@@ -197,6 +198,7 @@ var renderContent = (Root) => {
197
198
  numberOfAudioTags: 0,
198
199
  audioLatencyHint: window.remotion_audioLatencyHint ?? "playback",
199
200
  previewSampleRate: window.remotion_previewSampleRate,
201
+ _experimentalKeepAudioContextAlive: false,
200
202
  children: /* @__PURE__ */ jsx(Internals.RenderAssetManagerProvider, {
201
203
  collectAssets: null,
202
204
  children: /* @__PURE__ */ jsx(Root, {})
@@ -212,7 +214,7 @@ var renderContent = (Root) => {
212
214
  renderToDOM(/* @__PURE__ */ jsx("div", {
213
215
  children: /* @__PURE__ */ jsx(DelayedSpinner, {})
214
216
  }));
215
- import("./chunk-zpn4m49r.js").then(({ StudioInternals }) => {
217
+ import("./chunk-axbagh08.js").then(({ StudioInternals }) => {
216
218
  window.remotion_isStudio = true;
217
219
  window.remotion_isReadOnlyStudio = true;
218
220
  window.remotion_inputProps = "{}";
@@ -0,0 +1,7 @@
1
+ export declare const getCanvasCaptureImport: (file: File) => Promise<{
2
+ data: import("@remotion/studio-shared").CanvasCaptureData;
3
+ durationInSeconds: number;
4
+ file: File;
5
+ height: number;
6
+ width: number;
7
+ } | null>;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getCanvasCaptureImport = void 0;
4
+ const studio_shared_1 = require("@remotion/studio-shared");
5
+ const mediabunny_1 = require("mediabunny");
6
+ const getCanvasCaptureImport = async (file) => {
7
+ const input = new mediabunny_1.Input({
8
+ formats: mediabunny_1.ALL_FORMATS,
9
+ source: new mediabunny_1.BlobSource(file),
10
+ });
11
+ try {
12
+ const [metadata, videoTrack] = await Promise.all([
13
+ input.getMetadataTags(),
14
+ input.getPrimaryVideoTrack(),
15
+ ]);
16
+ const data = (0, studio_shared_1.parseCanvasCaptureData)(metadata);
17
+ if (data === null || videoTrack === null) {
18
+ return null;
19
+ }
20
+ const [durationFromMetadata, width, height] = await Promise.all([
21
+ input.getDurationFromMetadata(),
22
+ videoTrack.getDisplayWidth(),
23
+ videoTrack.getDisplayHeight(),
24
+ ]);
25
+ const durationInSeconds = durationFromMetadata !== null && durationFromMetadata !== void 0 ? durationFromMetadata : (await input.computeDuration([videoTrack]));
26
+ if (!Number.isFinite(durationInSeconds) ||
27
+ durationInSeconds <= 0 ||
28
+ !Number.isFinite(width) ||
29
+ width <= 0 ||
30
+ !Number.isFinite(height) ||
31
+ height <= 0) {
32
+ return null;
33
+ }
34
+ return { data, durationInSeconds, file, height, width };
35
+ }
36
+ catch (_a) {
37
+ return null;
38
+ }
39
+ finally {
40
+ input.dispose();
41
+ }
42
+ };
43
+ exports.getCanvasCaptureImport = getCanvasCaptureImport;
@@ -3,5 +3,5 @@ import type { ComboboxValue } from '../components/NewComposition/ComboBox';
3
3
  import type { OriginalPosition } from '../error-overlay/react-overlay/utils/get-source-map';
4
4
  export declare const getGitSourceName: (gitSource: GitSource) => string;
5
5
  export declare const getGitSourceBranchUrl: (gitSource: GitSource) => string;
6
- export declare const getGitRefUrl: (gitSource: GitSource, originalLocation: OriginalPosition) => string;
6
+ export declare const getGitRefUrl: (gitSource: GitSource, originalLocation: OriginalPosition, remotionRoot: string) => string;
7
7
  export declare const getGitMenuItem: () => ComboboxValue | null;
@@ -10,14 +10,44 @@ const getGitSourceName = (gitSource) => {
10
10
  exports.getGitSourceName = getGitSourceName;
11
11
  const getGitSourceBranchUrl = (gitSource) => {
12
12
  if (gitSource.type === 'github') {
13
- return `https://github.com/${gitSource.org}/${gitSource.name}/tree/${gitSource.ref}${gitSource.relativeFromGitRoot ? `/${gitSource.relativeFromGitRoot}` : ''}`;
13
+ const relativeFromGitRoot = gitSource.relativeFromGitRoot.replaceAll('\\', '/');
14
+ return `https://github.com/${gitSource.org}/${gitSource.name}/tree/${gitSource.ref}${relativeFromGitRoot ? `/${relativeFromGitRoot}` : ''}`;
14
15
  }
15
16
  throw new Error('Unknown git source type');
16
17
  };
17
18
  exports.getGitSourceBranchUrl = getGitSourceBranchUrl;
18
- const getGitRefUrl = (gitSource, originalLocation) => {
19
+ const getGitRefUrl = (gitSource, originalLocation, remotionRoot) => {
19
20
  if (gitSource.type === 'github') {
20
- return `https://github.com/${gitSource.org}/${gitSource.name}/tree/${gitSource.ref}/${gitSource.relativeFromGitRoot ? `${gitSource.relativeFromGitRoot}/` : ''}${originalLocation.source}#L${originalLocation.line}`;
21
+ if (!originalLocation.source) {
22
+ return (0, exports.getGitSourceBranchUrl)(gitSource);
23
+ }
24
+ const source = originalLocation.source.replaceAll('\\', '/');
25
+ const root = remotionRoot.replaceAll('\\', '/').replace(/\/+$/, '');
26
+ const shouldCompareCaseInsensitive = /^[a-z]:\//i.test(source) || /^[a-z]:\//i.test(root);
27
+ const comparableSource = shouldCompareCaseInsensitive
28
+ ? source.toLowerCase()
29
+ : source;
30
+ const comparableRoot = shouldCompareCaseInsensitive
31
+ ? root.toLowerCase()
32
+ : root;
33
+ const sourceIsInsideRoot = root.length > 0 && comparableSource.startsWith(`${comparableRoot}/`);
34
+ const relativeSource = sourceIsInsideRoot
35
+ ? source.slice(root.length + 1)
36
+ : source.replace(/^\.\/+/, '');
37
+ if (/^(?:[a-z]:\/|\/)/i.test(relativeSource)) {
38
+ return (0, exports.getGitSourceBranchUrl)(gitSource);
39
+ }
40
+ const filePath = [
41
+ gitSource.relativeFromGitRoot.replaceAll('\\', '/'),
42
+ relativeSource,
43
+ ]
44
+ .filter(Boolean)
45
+ .map((part) => part.replace(/^\/+|\/+$/g, ''))
46
+ .join('/');
47
+ const lineSuffix = originalLocation.line
48
+ ? `#L${originalLocation.line}`
49
+ : '';
50
+ return `https://github.com/${gitSource.org}/${gitSource.name}/blob/${gitSource.ref}/${filePath}${lineSuffix}`;
21
51
  }
22
52
  throw new Error('Unknown git source type');
23
53
  };
@@ -0,0 +1,12 @@
1
+ import type { SequenceNodePath } from 'remotion';
2
+ export type PendingInsertedElementSelection = {
3
+ compositionId: string;
4
+ nodePath: {
5
+ absolutePath: string;
6
+ nodePath: SequenceNodePath;
7
+ };
8
+ };
9
+ export declare const requestInsertedElementSelection: (selection: PendingInsertedElementSelection) => void;
10
+ export declare const clearInsertedElementSelection: (selection: PendingInsertedElementSelection) => void;
11
+ export declare const subscribeToInsertedElementSelection: (listener: () => void) => () => boolean;
12
+ export declare const getInsertedElementSelection: () => PendingInsertedElementSelection | null;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getInsertedElementSelection = exports.subscribeToInsertedElementSelection = exports.clearInsertedElementSelection = exports.requestInsertedElementSelection = void 0;
4
+ let pendingSelection = null;
5
+ const listeners = new Set();
6
+ const requestInsertedElementSelection = (selection) => {
7
+ pendingSelection = selection;
8
+ for (const listener of listeners) {
9
+ listener();
10
+ }
11
+ };
12
+ exports.requestInsertedElementSelection = requestInsertedElementSelection;
13
+ const clearInsertedElementSelection = (selection) => {
14
+ if (pendingSelection !== selection) {
15
+ return;
16
+ }
17
+ pendingSelection = null;
18
+ for (const listener of listeners) {
19
+ listener();
20
+ }
21
+ };
22
+ exports.clearInsertedElementSelection = clearInsertedElementSelection;
23
+ const subscribeToInsertedElementSelection = (listener) => {
24
+ listeners.add(listener);
25
+ return () => listeners.delete(listener);
26
+ };
27
+ exports.subscribeToInsertedElementSelection = subscribeToInsertedElementSelection;
28
+ const getInsertedElementSelection = () => pendingSelection;
29
+ exports.getInsertedElementSelection = getInsertedElementSelection;
@@ -4,6 +4,7 @@ import type { CodePosition, OriginalPosition } from '../error-overlay/react-over
4
4
  export declare const openInEditor: (stack: SymbolicatedStackFrame, editorId: EditorPickerId | null) => Promise<import("@remotion/studio-shared").OpenInEditorResponse>;
5
5
  export declare const openInCodingAgent: (codingAgentId: "claude-code" | "codex" | "copilot" | "cursor", prompt: string | null) => Promise<import("@remotion/studio-shared").OpenInCodingAgentResponse>;
6
6
  export declare const openInTerminal: (terminalId: TerminalId, directory: string) => Promise<OpenInTerminalResponse>;
7
+ export declare const openInGitClient: (gitClientId: "github-desktop") => Promise<import("@remotion/studio-shared").OpenInGitClientResponse>;
7
8
  export declare const openOriginalPositionInEditor: (originalPosition: OriginalPosition, editorId: EditorPickerId | null) => Promise<void>;
8
9
  export declare const openOriginalPositionInEditorAtProperty: ({ originalPosition, property, }: {
9
10
  originalPosition: CodePosition;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.openCompositionComponentInEditor = exports.preloadCompositionComponentInfo = exports.loadCompositionComponentInfo = exports.useCachedCompositionComponentInfo = exports.getCachedCompositionComponentInfo = exports.subscribeToCompositionComponentInfo = exports.openOriginalPositionInEditorAtProperty = exports.openOriginalPositionInEditor = exports.openInTerminal = exports.openInCodingAgent = exports.openInEditor = void 0;
3
+ exports.openCompositionComponentInEditor = exports.preloadCompositionComponentInfo = exports.loadCompositionComponentInfo = exports.useCachedCompositionComponentInfo = exports.getCachedCompositionComponentInfo = exports.subscribeToCompositionComponentInfo = exports.openOriginalPositionInEditorAtProperty = exports.openOriginalPositionInEditor = exports.openInGitClient = exports.openInTerminal = exports.openInCodingAgent = exports.openInEditor = void 0;
4
4
  const react_1 = require("react");
5
5
  const call_api_1 = require("../components/call-api");
6
6
  const browser_studio_operations_1 = require("./browser-studio-operations");
@@ -26,6 +26,10 @@ const openInTerminal = (terminalId, directory) => {
26
26
  return (0, call_api_1.callApi)('/api/open-in-terminal', { directory, terminalId });
27
27
  };
28
28
  exports.openInTerminal = openInTerminal;
29
+ const openInGitClient = (gitClientId) => {
30
+ return (0, call_api_1.callApi)('/api/open-in-git-client', { gitClientId });
31
+ };
32
+ exports.openInGitClient = openInGitClient;
29
33
  const openOriginalPositionInEditor = async (originalPosition, editorId) => {
30
34
  const response = await (0, exports.openInEditor)({
31
35
  originalColumnNumber: originalPosition.column,
@@ -1,9 +1,13 @@
1
1
  export type PointerSessionEndReason = 'pointerup' | 'pointercancel' | 'lostpointercapture' | 'blur' | 'visibilitychange' | 'buttons-released' | 'manual';
2
2
  type PointerSessionEvent = Pick<PointerEvent, 'button' | 'pointerId'>;
3
- export declare const startPointerSession: ({ event, target, onMove, onEnd, }: {
3
+ export declare const startCapturedPointerSession: ({ event, captureTarget, onMove, onEnd, }: {
4
4
  event: PointerSessionEvent;
5
- target: Element;
5
+ captureTarget: Element;
6
6
  onMove?: ((event: PointerEvent) => void) | undefined;
7
7
  onEnd: (reason: PointerSessionEndReason, event: PointerEvent | null) => void;
8
8
  }) => () => void;
9
+ export declare const observePointerRelease: ({ event, onEnd, }: {
10
+ event: PointerSessionEvent;
11
+ onEnd: (reason: PointerSessionEndReason, event: PointerEvent | null) => void;
12
+ }) => () => void;
9
13
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.startPointerSession = void 0;
3
+ exports.observePointerRelease = exports.startCapturedPointerSession = void 0;
4
4
  const getButtonMask = (button) => {
5
5
  if (button === 0) {
6
6
  return 1;
@@ -13,7 +13,7 @@ const getButtonMask = (button) => {
13
13
  }
14
14
  return 1 << button;
15
15
  };
16
- const startPointerSession = ({ event, target, onMove, onEnd, }) => {
16
+ const startPointerSession = ({ event, captureTarget, onMove, onEnd, }) => {
17
17
  var _a;
18
18
  const { pointerId } = event;
19
19
  const buttonMask = getButtonMask(event.button);
@@ -25,10 +25,10 @@ const startPointerSession = ({ event, target, onMove, onEnd, }) => {
25
25
  window.removeEventListener('pointercancel', handleCancel);
26
26
  window.removeEventListener('blur', handleBlur);
27
27
  document.removeEventListener('visibilitychange', handleVisibilityChange);
28
- target.removeEventListener('lostpointercapture', handleLostPointerCapture);
29
- if ((_a = target.hasPointerCapture) === null || _a === void 0 ? void 0 : _a.call(target, pointerId)) {
28
+ captureTarget === null || captureTarget === void 0 ? void 0 : captureTarget.removeEventListener('lostpointercapture', handleLostPointerCapture);
29
+ if ((_a = captureTarget === null || captureTarget === void 0 ? void 0 : captureTarget.hasPointerCapture) === null || _a === void 0 ? void 0 : _a.call(captureTarget, pointerId)) {
30
30
  try {
31
- target.releasePointerCapture(pointerId);
31
+ captureTarget.releasePointerCapture(pointerId);
32
32
  }
33
33
  catch (_b) {
34
34
  // The browser may already have released capture.
@@ -77,18 +77,35 @@ const startPointerSession = ({ event, target, onMove, onEnd, }) => {
77
77
  end('visibilitychange', null);
78
78
  }
79
79
  }
80
- try {
81
- (_a = target.setPointerCapture) === null || _a === void 0 ? void 0 : _a.call(target, pointerId);
82
- }
83
- catch (_b) {
84
- // Capture is best-effort for detached or browser-owned targets.
80
+ if (captureTarget) {
81
+ try {
82
+ (_a = captureTarget.setPointerCapture) === null || _a === void 0 ? void 0 : _a.call(captureTarget, pointerId);
83
+ }
84
+ catch (_b) {
85
+ // Capture is best-effort for detached targets.
86
+ }
87
+ captureTarget.addEventListener('lostpointercapture', handleLostPointerCapture);
85
88
  }
86
89
  window.addEventListener('pointermove', handleMove);
87
90
  window.addEventListener('pointerup', handleUp);
88
91
  window.addEventListener('pointercancel', handleCancel);
89
92
  window.addEventListener('blur', handleBlur);
90
93
  document.addEventListener('visibilitychange', handleVisibilityChange);
91
- target.addEventListener('lostpointercapture', handleLostPointerCapture);
92
94
  return () => end('manual', null);
93
95
  };
94
- exports.startPointerSession = startPointerSession;
96
+ // Use for a gesture owned by a Studio control, such as a slider or resize
97
+ // handle. `captureTarget` is normally the pointerdown handler's currentTarget.
98
+ const startCapturedPointerSession = ({ event, captureTarget, onMove, onEnd, }) => {
99
+ return startPointerSession({ event, captureTarget, onMove, onEnd });
100
+ };
101
+ exports.startCapturedPointerSession = startCapturedPointerSession;
102
+ // Use when observing a gesture that started elsewhere. This deliberately does
103
+ // not capture the pointer from the element under it.
104
+ const observePointerRelease = ({ event, onEnd, }) => {
105
+ return startPointerSession({
106
+ event,
107
+ captureTarget: null,
108
+ onEnd,
109
+ });
110
+ };
111
+ exports.observePointerRelease = observePointerRelease;
@@ -1,6 +1,6 @@
1
- import type { SymbolicatedStackFrame } from '@remotion/studio-shared';
1
+ import type { CanvasCaptureData, SymbolicatedStackFrame } from '@remotion/studio-shared';
2
2
  export declare const getUniqueCompositionName: (compositions: import("remotion").AnyComposition[]) => string;
3
- export declare const useCreateComposition: ({ compositions, durationInFrames, folderName, newId, parentName, selectedFrameRate, size, }: {
3
+ export declare const useCreateComposition: ({ compositions, durationInFrames, folderName, newId, parentName, selectedFrameRate, size, canvasCapture, }: {
4
4
  compositions: import("remotion").AnyComposition[];
5
5
  durationInFrames: number;
6
6
  folderName: string | null;
@@ -11,6 +11,12 @@ export declare const useCreateComposition: ({ compositions, durationInFrames, fo
11
11
  width: number;
12
12
  height: number;
13
13
  };
14
+ canvasCapture: {
15
+ readonly data: CanvasCaptureData;
16
+ readonly videoFileName: string;
17
+ readonly videoHeight: number;
18
+ readonly videoWidth: number;
19
+ } | null;
14
20
  }) => {
15
21
  codemod: {
16
22
  type: "new-composition";
@@ -23,6 +29,13 @@ export declare const useCreateComposition: ({ compositions, durationInFrames, fo
23
29
  newWidth: number;
24
30
  newFps: number;
25
31
  newDurationInFrames: number;
32
+ canvasCapture: {
33
+ readonly videoFileName: string;
34
+ readonly videoHeight: number;
35
+ readonly videoWidth: number;
36
+ readonly keyframeFps: number;
37
+ readonly data: CanvasCaptureData;
38
+ } | null;
26
39
  };
27
40
  createComposition: ({ signal, symbolicatedStack, }: {
28
41
  signal: AbortSignal;
@@ -19,21 +19,6 @@ const toPascalCase = (value) => {
19
19
  }
20
20
  return candidate;
21
21
  };
22
- const waitForComposition = (compositionId) => {
23
- return new Promise((resolve) => {
24
- const started = Date.now();
25
- const interval = window.setInterval(() => {
26
- var _a;
27
- var _b;
28
- const compositionNames = (_b = (_a = window.remotion_getCompositionNames) === null || _a === void 0 ? void 0 : _a.call(window)) !== null && _b !== void 0 ? _b : [];
29
- if (compositionNames.includes(compositionId) ||
30
- Date.now() - started > 10000) {
31
- window.clearInterval(interval);
32
- resolve();
33
- }
34
- }, 100);
35
- });
36
- };
37
22
  const getUniqueCompositionName = (compositions) => {
38
23
  let counter = 1;
39
24
  while (true) {
@@ -46,7 +31,7 @@ const getUniqueCompositionName = (compositions) => {
46
31
  }
47
32
  };
48
33
  exports.getUniqueCompositionName = getUniqueCompositionName;
49
- const useCreateComposition = ({ compositions, durationInFrames, folderName, newId, parentName, selectedFrameRate, size, }) => {
34
+ const useCreateComposition = ({ compositions, durationInFrames, folderName, newId, parentName, selectedFrameRate, size, canvasCapture, }) => {
50
35
  const selectComposition = (0, InitialCompositionLoader_1.useSelectComposition)();
51
36
  const componentName = (0, react_1.useMemo)(() => toPascalCase(newId), [newId]);
52
37
  const nameValidationMessage = (0, react_1.useMemo)(() => {
@@ -70,8 +55,18 @@ const useCreateComposition = ({ compositions, durationInFrames, folderName, newI
70
55
  componentImportPath: `./${componentName}`,
71
56
  folderName,
72
57
  parentName,
58
+ canvasCapture: canvasCapture === null
59
+ ? null
60
+ : {
61
+ data: canvasCapture.data,
62
+ keyframeFps: Number(selectedFrameRate),
63
+ videoFileName: canvasCapture.videoFileName,
64
+ videoHeight: canvasCapture.videoHeight,
65
+ videoWidth: canvasCapture.videoWidth,
66
+ },
73
67
  };
74
68
  }, [
69
+ canvasCapture,
75
70
  componentName,
76
71
  durationInFrames,
77
72
  folderName,
@@ -92,7 +87,6 @@ const useCreateComposition = ({ compositions, durationInFrames, folderName, newI
92
87
  symbolicatedStack,
93
88
  });
94
89
  if (result.success) {
95
- await waitForComposition(newId);
96
90
  selectComposition({
97
91
  id: newId,
98
92
  folderName,
@@ -61,6 +61,7 @@ const getFileMenu = ({ readOnlyStudio, closeMenu, editorName, previewServerState
61
61
  folderName: null,
62
62
  parentName: null,
63
63
  stack: null,
64
+ canvasCapture: null,
64
65
  });
65
66
  },
66
67
  type: 'item',
@@ -0,0 +1,6 @@
1
+ import type { GitClientId } from '@remotion/studio-shared';
2
+ import React from 'react';
3
+ export declare const GitClientIcon: React.FC<{
4
+ readonly gitClientId: GitClientId;
5
+ readonly size: number;
6
+ }>;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GitClientIcon = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const GitClientIcon = ({ gitClientId, size }) => {
6
+ return (jsx_runtime_1.jsx("img", { alt: "", "aria-hidden": true, "data-git-client-icon": gitClientId, draggable: false, src: `/api/app-icon/git-client/${gitClientId}.png`, style: { flexShrink: 0, height: size, width: size } }));
7
+ };
8
+ exports.GitClientIcon = GitClientIcon;
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ export declare const GitHubIcon: React.FC<{
3
+ readonly size: number;
4
+ }>;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GitHubIcon = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const colors_1 = require("../helpers/colors");
6
+ // Brand shape adapted from Simple Icons (CC0 1.0):
7
+ // https://github.com/simple-icons/simple-icons/blob/develop/icons/github.svg
8
+ const GitHubIcon = ({ size }) => {
9
+ return (jsx_runtime_1.jsx("svg", { "aria-hidden": true, "data-github-icon": true, style: { flexShrink: 0, height: size, width: size }, viewBox: "0 0 24 24", children: jsx_runtime_1.jsx("path", { d: "M12 .3a12 12 0 0 0-3.79 23.39c.6.11.82-.26.82-.58v-2.23c-3.34.73-4.04-1.42-4.04-1.42-.55-1.39-1.34-1.76-1.34-1.76-1.09-.75.08-.73.08-.73 1.2.09 1.84 1.24 1.84 1.24 1.07 1.83 2.81 1.3 3.5.99.11-.78.42-1.31.76-1.61-2.67-.31-5.47-1.34-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.13-.3-.54-1.52.11-3.18 0 0 1.01-.32 3.3 1.23a11.5 11.5 0 0 1 6 0c2.29-1.55 3.3-1.23 3.3-1.23.65 1.66.24 2.88.12 3.18a4.65 4.65 0 0 1 1.23 3.22c0 4.6-2.81 5.62-5.48 5.92.43.37.81 1.1.81 2.22v3.3c0 .32.22.7.82.58A12 12 0 0 0 12 .3Z", fill: colors_1.CURRENT_COLOR }) }));
10
+ };
11
+ exports.GitHubIcon = GitHubIcon;
@@ -187,13 +187,13 @@ const renderContent = (Root) => {
187
187
  defaultPixelFormat: bundleMode.compositionDefaultPixelFormat,
188
188
  defaultProResProfile: bundleMode.compositionDefaultProResProfile,
189
189
  defaultSampleRate: bundleMode.compositionDefaultSampleRate,
190
- }, initialCompositions: [], children: jsx_runtime_1.jsx(remotion_1.Internals.RemotionRootContexts, { frameState: null, audioEnabled: window.remotion_audioEnabled, videoEnabled: window.remotion_videoEnabled, logLevel: (_a = window.remotion_logLevel) !== null && _a !== void 0 ? _a : 'info', numberOfAudioTags: 0, audioLatencyHint: (_b = window.remotion_audioLatencyHint) !== null && _b !== void 0 ? _b : 'playback', previewSampleRate: window.remotion_previewSampleRate, children: jsx_runtime_1.jsxs(remotion_1.Internals.RenderAssetManagerProvider, { collectAssets: null, children: [
190
+ }, initialCompositions: [], children: jsx_runtime_1.jsx(remotion_1.Internals.RemotionRootContexts, { frameState: null, audioEnabled: window.remotion_audioEnabled, videoEnabled: window.remotion_videoEnabled, logLevel: (_a = window.remotion_logLevel) !== null && _a !== void 0 ? _a : 'info', numberOfAudioTags: 0, audioLatencyHint: (_b = window.remotion_audioLatencyHint) !== null && _b !== void 0 ? _b : 'playback', previewSampleRate: window.remotion_previewSampleRate, _experimentalKeepAudioContextAlive: false, children: jsx_runtime_1.jsxs(remotion_1.Internals.RenderAssetManagerProvider, { collectAssets: null, children: [
191
191
  jsx_runtime_1.jsx(Root, {}), jsx_runtime_1.jsx(GetVideoComposition, { state: bundleMode })
192
192
  ] }) }) }));
193
193
  renderToDOM(markup);
194
194
  }
195
195
  if (bundleMode.type === 'evaluation') {
196
- const markup = (jsx_runtime_1.jsx(remotion_1.Internals.CompositionManagerProvider, { initialCanvasContent: null, onlyRenderComposition: null, currentCompositionMetadata: null, initialCompositions: [], children: jsx_runtime_1.jsx(remotion_1.Internals.RemotionRootContexts, { frameState: null, audioEnabled: window.remotion_audioEnabled, videoEnabled: window.remotion_videoEnabled, logLevel: (_c = window.remotion_logLevel) !== null && _c !== void 0 ? _c : 'info', numberOfAudioTags: 0, audioLatencyHint: (_d = window.remotion_audioLatencyHint) !== null && _d !== void 0 ? _d : 'playback', previewSampleRate: window.remotion_previewSampleRate, children: jsx_runtime_1.jsx(remotion_1.Internals.RenderAssetManagerProvider, { collectAssets: null, children: jsx_runtime_1.jsx(Root, {}) }) }) }));
196
+ const markup = (jsx_runtime_1.jsx(remotion_1.Internals.CompositionManagerProvider, { initialCanvasContent: null, onlyRenderComposition: null, currentCompositionMetadata: null, initialCompositions: [], children: jsx_runtime_1.jsx(remotion_1.Internals.RemotionRootContexts, { frameState: null, audioEnabled: window.remotion_audioEnabled, videoEnabled: window.remotion_videoEnabled, logLevel: (_c = window.remotion_logLevel) !== null && _c !== void 0 ? _c : 'info', numberOfAudioTags: 0, audioLatencyHint: (_d = window.remotion_audioLatencyHint) !== null && _d !== void 0 ? _d : 'playback', previewSampleRate: window.remotion_previewSampleRate, _experimentalKeepAudioContextAlive: false, children: jsx_runtime_1.jsx(remotion_1.Internals.RenderAssetManagerProvider, { collectAssets: null, children: jsx_runtime_1.jsx(Root, {}) }) }) }));
197
197
  renderToDOM(markup);
198
198
  }
199
199
  if (bundleMode.type === 'index') {
@@ -1,6 +1,6 @@
1
1
  import type { AudioCodec, ChromeMode, Codec, ColorSpace, LogLevel, OpenGlRenderer, PixelFormat, StillImageFormat, VideoImageFormat, X264Preset } from '@remotion/renderer';
2
2
  import type { HardwareAccelerationOption } from '@remotion/renderer/client';
3
- import type { PackageManager, RenderDefaults } from '@remotion/studio-shared';
3
+ import type { CanvasCaptureData, PackageManager, RenderDefaults } from '@remotion/studio-shared';
4
4
  import type { RenderStillOnWebImageFormat, WebRendererAudioCodec, WebRendererContainer, WebRendererHardwareAcceleration, WebRendererPageResponsiveness, WebRendererQuality, WebRendererVideoCodec } from '@remotion/web-renderer';
5
5
  import type React from 'react';
6
6
  import type { SequencePropsSubscriptionKey, _InternalTypes } from 'remotion';
@@ -109,11 +109,19 @@ export type AddEffectModalState = {
109
109
  nodePath: SequencePropsSubscriptionKey;
110
110
  clientId: string;
111
111
  };
112
+ export type CanvasCaptureImport = {
113
+ readonly data: CanvasCaptureData;
114
+ readonly durationInSeconds: number;
115
+ readonly file: File;
116
+ readonly height: number;
117
+ readonly width: number;
118
+ };
112
119
  export type ModalState = {
113
120
  type: 'new-comp';
114
121
  folderName: string | null;
115
122
  parentName: string | null;
116
123
  stack: string | null;
124
+ canvasCapture: CanvasCaptureImport | null;
117
125
  } | {
118
126
  type: 'new-folder';
119
127
  parentName: string | null;
@@ -5,7 +5,6 @@ export declare const HigherZIndex: React.FC<{
5
5
  readonly children: React.ReactNode;
6
6
  readonly disabled?: boolean;
7
7
  readonly outsideClickButton?: 'any' | 'primary';
8
- readonly stackOnHighest?: boolean;
9
8
  }>;
10
9
  export declare const useZIndex: () => {
11
10
  currentZIndex: number;
@@ -15,7 +15,7 @@ const margin = {
15
15
  };
16
16
  const EscapeHook = ({ onEscape }) => {
17
17
  const keybindings = (0, use_keybinding_1.useKeybinding)();
18
- (0, react_1.useEffect)(() => {
18
+ (0, react_1.useLayoutEffect)(() => {
19
19
  const escape = keybindings.registerKeybinding({
20
20
  event: 'keydown',
21
21
  key: 'Escape',
@@ -32,80 +32,74 @@ const EscapeHook = ({ onEscape }) => {
32
32
  }, [keybindings, onEscape]);
33
33
  return null;
34
34
  };
35
- const HigherZIndex = ({ children, onEscape, onOutsideClick, disabled, outsideClickButton = 'any', stackOnHighest = false, }) => {
36
- var _a;
35
+ const HigherZIndex = ({ children, onEscape, onOutsideClick, disabled, outsideClickButton = 'any', }) => {
37
36
  const context = (0, react_1.useContext)(ZIndexContext);
38
37
  const highestContext = (0, react_1.useContext)(highest_z_index_1.HighestZIndexContext);
38
+ const { registerZIndex, unregisterZIndex } = highestContext;
39
39
  const containerRef = (0, react_1.useRef)(null);
40
- const stackedIndex = (0, react_1.useRef)(null);
41
- if (disabled || !stackOnHighest) {
42
- stackedIndex.current = null;
43
- }
44
- else if (stackedIndex.current === null) {
45
- stackedIndex.current =
46
- Math.max(context.currentIndex, highestContext.highestIndex) + 1;
47
- }
40
+ const highestIndexRef = (0, react_1.useRef)(highestContext.highestIndex);
41
+ const onOutsideClickRef = (0, react_1.useRef)(onOutsideClick);
42
+ const outsideClickButtonRef = (0, react_1.useRef)(outsideClickButton);
48
43
  const currentIndex = disabled
49
44
  ? context.currentIndex
50
- : ((_a = stackedIndex.current) !== null && _a !== void 0 ? _a : context.currentIndex + 1);
51
- (0, react_1.useEffect)(() => {
45
+ : context.currentIndex + 1;
46
+ (0, react_1.useLayoutEffect)(() => {
47
+ highestIndexRef.current = highestContext.highestIndex;
48
+ onOutsideClickRef.current = onOutsideClick;
49
+ outsideClickButtonRef.current = outsideClickButton;
50
+ }, [highestContext.highestIndex, onOutsideClick, outsideClickButton]);
51
+ (0, react_1.useLayoutEffect)(() => {
52
52
  if (disabled) {
53
53
  return;
54
54
  }
55
- highestContext.registerZIndex(currentIndex);
56
- return () => highestContext.unregisterZIndex(currentIndex);
57
- }, [currentIndex, highestContext, disabled]);
58
- (0, react_1.useEffect)(() => {
55
+ registerZIndex(currentIndex);
56
+ return () => unregisterZIndex(currentIndex);
57
+ }, [currentIndex, disabled, registerZIndex, unregisterZIndex]);
58
+ (0, react_1.useLayoutEffect)(() => {
59
59
  if (disabled) {
60
60
  return;
61
61
  }
62
62
  let endPointerSession = null;
63
63
  const listener = (downEvent) => {
64
64
  var _a;
65
- if (outsideClickButton === 'primary' && downEvent.button !== 0) {
65
+ if (outsideClickButtonRef.current === 'primary' &&
66
+ downEvent.button !== 0) {
66
67
  return;
67
68
  }
68
69
  const outsideClick = !((_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.contains(downEvent.target));
69
70
  if (!outsideClick) {
70
71
  return;
71
72
  }
73
+ // The topmost layer owns the full pointer gesture. Layers may mount or
74
+ // unmount before pointerup, but that must not cancel or transfer dismissal.
75
+ if (highestIndexRef.current !== currentIndex) {
76
+ return;
77
+ }
72
78
  endPointerSession === null || endPointerSession === void 0 ? void 0 : endPointerSession();
73
- endPointerSession = (0, pointer_session_1.startPointerSession)({
79
+ endPointerSession = (0, pointer_session_1.observePointerRelease)({
74
80
  event: downEvent,
75
- target: downEvent.target,
76
81
  onEnd: (reason, upEvent) => {
77
82
  var _a;
78
83
  endPointerSession = null;
79
84
  if ((reason === 'pointerup' || reason === 'buttons-released') &&
80
85
  upEvent &&
81
- highestContext.highestIndex === currentIndex &&
82
86
  !(0, input_dragger_click_lock_1.getClickLock)()) {
83
87
  const target = (_a = document.elementFromPoint(upEvent.clientX, upEvent.clientY)) !== null && _a !== void 0 ? _a : upEvent.target;
84
88
  if (document.contains(target)) {
85
89
  upEvent.stopPropagation();
86
- onOutsideClick(target);
90
+ onOutsideClickRef.current(target);
87
91
  }
88
92
  }
89
93
  },
90
94
  });
91
95
  };
92
- // The capture phase for the pointerdown that opened this layer has already
93
- // passed, so installing the listener immediately cannot dismiss the new layer.
94
- // Waiting for the next animation frame would leave a window in which a fast
95
- // outside click is missed.
96
96
  window.addEventListener('pointerdown', listener, true);
97
97
  return () => {
98
98
  endPointerSession === null || endPointerSession === void 0 ? void 0 : endPointerSession();
99
99
  endPointerSession = null;
100
100
  return window.removeEventListener('pointerdown', listener, true);
101
101
  };
102
- }, [
103
- currentIndex,
104
- disabled,
105
- highestContext.highestIndex,
106
- onOutsideClick,
107
- outsideClickButton,
108
- ]);
102
+ }, [currentIndex, disabled]);
109
103
  const value = (0, react_1.useMemo)(() => {
110
104
  return {
111
105
  currentIndex,