@remotion/studio 4.0.513 → 4.0.515

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 (204) hide show
  1. package/dist/Studio.js +2 -1
  2. package/dist/api/install-package.js +14 -1
  3. package/dist/components/AskAiModal.js +1 -1
  4. package/dist/components/AssetSelector.js +2 -0
  5. package/dist/components/AssetSelectorItem.d.ts +2 -1
  6. package/dist/components/AssetSelectorItem.js +19 -16
  7. package/dist/components/AudioWaveform.d.ts +3 -0
  8. package/dist/components/AudioWaveform.js +65 -199
  9. package/dist/components/Canvas.js +40 -5
  10. package/dist/components/CanvasOrLoading.js +5 -0
  11. package/dist/components/CompositionSelector.js +2 -0
  12. package/dist/components/ConfigureDefaultEditorModal.js +17 -11
  13. package/dist/components/Editor.js +16 -1
  14. package/dist/components/ElementInstallConfirmation.d.ts +1 -0
  15. package/dist/components/ElementInstallConfirmation.js +4 -2
  16. package/dist/components/FixComputedValueModal.d.ts +9 -0
  17. package/dist/components/FixComputedValueModal.js +217 -0
  18. package/dist/components/InitialCompositionLoader.js +19 -0
  19. package/dist/components/InlineDropdown.js +4 -4
  20. package/dist/components/InspectorInfoHeader.js +3 -3
  21. package/dist/components/InspectorOpenInEditor.js +3 -1
  22. package/dist/components/InspectorPanel/CompositionInspector.js +37 -5
  23. package/dist/components/InspectorPanel/CompositionInspectorHeader.js +2 -2
  24. package/dist/components/InspectorPanel/ConnectedCompositionsSection.js +1 -1
  25. package/dist/components/InspectorPanel/EasingInspector.js +1 -1
  26. package/dist/components/InspectorPanel/KeyframeInspector.js +2 -2
  27. package/dist/components/InspectorPanel/SequenceInspectorHeader.js +1 -1
  28. package/dist/components/InspectorPanel/SequenceSelectionInspector.js +35 -6
  29. package/dist/components/InspectorPanel/common.d.ts +5 -5
  30. package/dist/components/InspectorPanel/common.js +8 -8
  31. package/dist/components/InspectorPanel/styles.d.ts +1 -1
  32. package/dist/components/InspectorPanel/styles.js +4 -2
  33. package/dist/components/InspectorSourceLocation.d.ts +1 -1
  34. package/dist/components/InspectorSourceLocation.js +2 -2
  35. package/dist/components/InstallPackage.d.ts +1 -1
  36. package/dist/components/InstallPackage.js +38 -6
  37. package/dist/components/LicenseKeyValidation.js +2 -2
  38. package/dist/components/Menu/MenuDivider.js +1 -1
  39. package/dist/components/Modals.js +11 -3
  40. package/dist/components/NewComposition/CodemodFooter.d.ts +1 -0
  41. package/dist/components/NewComposition/CodemodFooter.js +10 -8
  42. package/dist/components/NewComposition/DeleteComposition.js +1 -1
  43. package/dist/components/NewComposition/DeleteFolder.js +1 -1
  44. package/dist/components/NewComposition/DuplicateComposition.js +7 -2
  45. package/dist/components/NewComposition/NewComposition.js +1 -1
  46. package/dist/components/NewComposition/NewFolder.js +1 -1
  47. package/dist/components/NewComposition/RenameComposition.js +1 -1
  48. package/dist/components/NewComposition/RenameFolder.js +1 -1
  49. package/dist/components/NewComposition/duplicate-composition-api.d.ts +16 -0
  50. package/dist/components/NewComposition/duplicate-composition-api.js +16 -0
  51. package/dist/components/NoRegisterRoot.js +1 -1
  52. package/dist/components/QuickSwitcher/ExplorerQuickSwitcherTrigger.js +4 -3
  53. package/dist/components/QuickSwitcher/QuickSwitcherContent.js +1 -0
  54. package/dist/components/QuickSwitcher/fuzzy-search.js +1 -1
  55. package/dist/components/RefreshCompositionOverlay.js +1 -1
  56. package/dist/components/RenderButton.js +6 -3
  57. package/dist/components/RenderModal/GuiRenderStatus.js +3 -1
  58. package/dist/components/RenderModal/RenderModalOutputName.js +3 -3
  59. package/dist/components/RenderModal/WebRenderModal.js +10 -4
  60. package/dist/components/RenderModal/WebRenderModalAdvanced.d.ts +2 -0
  61. package/dist/components/RenderModal/WebRenderModalAdvanced.js +9 -2
  62. package/dist/components/RenderModal/render-modals.js +5 -1
  63. package/dist/components/RenderQueue/ClientRenderQueueProcessor.js +5 -1
  64. package/dist/components/RenderQueue/RenderQueueItem.js +3 -1
  65. package/dist/components/RenderQueue/RenderQueueOpenInFolder.js +3 -1
  66. package/dist/components/RenderQueue/RenderQueueRemoveItem.js +1 -1
  67. package/dist/components/RenderQueue/actions.js +12 -2
  68. package/dist/components/RenderQueue/client-side-render-types.d.ts +1 -0
  69. package/dist/components/RenderingSettings.d.ts +2 -0
  70. package/dist/components/RenderingSettings.js +355 -0
  71. package/dist/components/SelectedOutlineCanvasRotation.d.ts +1 -0
  72. package/dist/components/SelectedOutlineCanvasRotation.js +11 -4
  73. package/dist/components/SelectedOutlineElement.js +3 -3
  74. package/dist/components/SelectedOutlinePolygon.d.ts +2 -0
  75. package/dist/components/SelectedOutlinePolygon.js +54 -9
  76. package/dist/components/SettingsContext.d.ts +4 -1
  77. package/dist/components/SettingsContext.js +9 -2
  78. package/dist/components/SettingsModal.d.ts +1 -1
  79. package/dist/components/SettingsModal.js +10 -1
  80. package/dist/components/SettingsModalFooter.js +1 -1
  81. package/dist/components/StudioSettings.d.ts +2 -0
  82. package/dist/components/StudioSettings.js +185 -0
  83. package/dist/components/Timeline/LoopedIndicator.js +1 -1
  84. package/dist/components/Timeline/LoopedTimelineIndicators.d.ts +3 -0
  85. package/dist/components/Timeline/LoopedTimelineIndicators.js +22 -14
  86. package/dist/components/Timeline/Timeline.js +11 -9
  87. package/dist/components/Timeline/TimelineAssetField.d.ts +2 -2
  88. package/dist/components/Timeline/TimelineAssetField.js +1 -1
  89. package/dist/components/Timeline/TimelineClipboardKeybindings.js +6 -2
  90. package/dist/components/Timeline/TimelineDragHandler.js +62 -76
  91. package/dist/components/Timeline/TimelineEffectItem.js +23 -6
  92. package/dist/components/Timeline/TimelineEffectPropItem.d.ts +4 -2
  93. package/dist/components/Timeline/TimelineEffectPropItem.js +22 -13
  94. package/dist/components/Timeline/TimelineExpandArrowButton.js +10 -7
  95. package/dist/components/Timeline/TimelineExpandedKeyframeRow.js +4 -1
  96. package/dist/components/Timeline/TimelineExpandedRow.d.ts +1 -1
  97. package/dist/components/Timeline/TimelineExpandedRow.js +4 -4
  98. package/dist/components/Timeline/TimelineExpandedSection.js +8 -5
  99. package/dist/components/Timeline/TimelineHeightContainer.d.ts +0 -4
  100. package/dist/components/Timeline/TimelineHeightContainer.js +4 -4
  101. package/dist/components/Timeline/TimelineImageInfo.d.ts +1 -0
  102. package/dist/components/Timeline/TimelineImageInfo.js +3 -2
  103. package/dist/components/Timeline/TimelineKeyframeControls.js +6 -1
  104. package/dist/components/Timeline/TimelineList.d.ts +1 -5
  105. package/dist/components/Timeline/TimelineList.js +14 -9
  106. package/dist/components/Timeline/TimelinePinchZoom.js +5 -5
  107. package/dist/components/Timeline/TimelineRowChrome.d.ts +1 -2
  108. package/dist/components/Timeline/TimelineRowChrome.js +50 -7
  109. package/dist/components/Timeline/TimelineSchemaField.d.ts +7 -3
  110. package/dist/components/Timeline/TimelineSchemaField.js +71 -13
  111. package/dist/components/Timeline/TimelineScrollable.js +3 -1
  112. package/dist/components/Timeline/TimelineSelection.d.ts +13 -5
  113. package/dist/components/Timeline/TimelineSelection.js +95 -106
  114. package/dist/components/Timeline/TimelineSequence.js +59 -25
  115. package/dist/components/Timeline/TimelineSequenceItem.d.ts +1 -1
  116. package/dist/components/Timeline/TimelineSequenceItem.js +21 -12
  117. package/dist/components/Timeline/TimelineSequenceName.js +1 -1
  118. package/dist/components/Timeline/TimelineSequencePropItem.d.ts +2 -1
  119. package/dist/components/Timeline/TimelineSequencePropItem.js +3 -3
  120. package/dist/components/Timeline/TimelineSequenceRightEdgeDragHandle.js +33 -10
  121. package/dist/components/Timeline/TimelineSlider.js +42 -33
  122. package/dist/components/Timeline/TimelineTimeIndicators.js +90 -98
  123. package/dist/components/Timeline/TimelineTrack.js +4 -1
  124. package/dist/components/Timeline/TimelineTracks.d.ts +0 -2
  125. package/dist/components/Timeline/TimelineTracks.js +11 -4
  126. package/dist/components/Timeline/TimelineTransformOriginField.js +1 -1
  127. package/dist/components/Timeline/TimelineVideoInfo.d.ts +4 -4
  128. package/dist/components/Timeline/TimelineVideoInfo.js +155 -63
  129. package/dist/components/Timeline/TimelineViewport.d.ts +12 -0
  130. package/dist/components/Timeline/TimelineViewport.js +62 -0
  131. package/dist/components/Timeline/TimelineVirtualization.d.ts +22 -0
  132. package/dist/components/Timeline/TimelineVirtualization.js +144 -0
  133. package/dist/components/Timeline/TimelineZoomControls.js +16 -8
  134. package/dist/components/Timeline/call-add-keyframe.js +31 -3
  135. package/dist/components/Timeline/call-delete-keyframe.js +13 -3
  136. package/dist/components/Timeline/call-move-keyframe.js +11 -1
  137. package/dist/components/Timeline/call-update-keyframe-settings.js +31 -3
  138. package/dist/components/Timeline/delete-selected-timeline-item.js +8 -3
  139. package/dist/components/Timeline/duplicate-selected-timeline-item.js +4 -2
  140. package/dist/components/Timeline/get-timeline-keyframes.d.ts +1 -2
  141. package/dist/components/Timeline/get-timeline-keyframes.js +1 -8
  142. package/dist/components/Timeline/get-timeline-sequence-visible-layout.d.ts +30 -0
  143. package/dist/components/Timeline/get-timeline-sequence-visible-layout.js +61 -0
  144. package/dist/components/Timeline/save-effect-prop.js +3 -3
  145. package/dist/components/Timeline/split-selected-timeline-item.js +2 -2
  146. package/dist/components/Timeline/timeline-scroll-logic.d.ts +24 -0
  147. package/dist/components/Timeline/timeline-scroll-logic.js +127 -4
  148. package/dist/components/Timeline/use-timeline-height.d.ts +3 -5
  149. package/dist/components/Timeline/use-timeline-height.js +16 -18
  150. package/dist/components/UpdateCheck.d.ts +2 -10
  151. package/dist/components/UpdateCheck.js +8 -2
  152. package/dist/components/VisualControls/VisualControlHandle.js +8 -0
  153. package/dist/components/delete-jsx-node-api.d.ts +2 -0
  154. package/dist/components/delete-jsx-node-api.js +12 -0
  155. package/dist/components/effect-drag-and-drop.js +6 -3
  156. package/dist/components/effect-operations-api.d.ts +8 -0
  157. package/dist/components/effect-operations-api.js +52 -0
  158. package/dist/components/element-install-api.d.ts +3 -0
  159. package/dist/components/element-install-api.js +19 -0
  160. package/dist/components/import-assets.js +20 -9
  161. package/dist/components/reorder-sequence-api.d.ts +2 -0
  162. package/dist/components/reorder-sequence-api.js +12 -0
  163. package/dist/components/split-jsx-sequence-api.d.ts +2 -0
  164. package/dist/components/split-jsx-sequence-api.js +12 -0
  165. package/dist/components/split-video-from-audio-api.d.ts +2 -0
  166. package/dist/components/split-video-from-audio-api.js +12 -0
  167. package/dist/error-overlay/remotion-overlay/Overlay.js +1 -1
  168. package/dist/error-overlay/remotion-overlay/log-studio-error.js +4 -0
  169. package/dist/esm/{chunk-r46yxrjr.js → chunk-s6k99gh2.js} +1 -1
  170. package/dist/esm/{chunk-tv7r0jy3.js → chunk-v5a8vpv4.js} +11479 -7851
  171. package/dist/esm/index.mjs +7 -5
  172. package/dist/esm/internals.mjs +11479 -7851
  173. package/dist/esm/previewEntry.mjs +11311 -7683
  174. package/dist/esm/renderEntry.mjs +3 -4
  175. package/dist/helpers/browser-studio-operations.d.ts +5 -0
  176. package/dist/helpers/browser-studio-operations.js +27 -1
  177. package/dist/helpers/colors.d.ts +1 -1
  178. package/dist/helpers/colors.js +1 -1
  179. package/dist/helpers/get-timeline-max-zoom.d.ts +26 -0
  180. package/dist/helpers/get-timeline-max-zoom.js +45 -0
  181. package/dist/helpers/inject-css.js +7 -5
  182. package/dist/helpers/render-codec-label.d.ts +1 -1
  183. package/dist/helpers/retry-payload.js +1 -0
  184. package/dist/helpers/studio-runtime-config.d.ts +1 -1
  185. package/dist/helpers/studio-runtime-config.js +2 -3
  186. package/dist/helpers/timeline-layout.d.ts +3 -1
  187. package/dist/helpers/timeline-layout.js +6 -1
  188. package/dist/helpers/url-state.js +3 -2
  189. package/dist/helpers/use-max-media-duration.js +5 -3
  190. package/dist/helpers/use-media-metadata.js +37 -20
  191. package/dist/helpers/use-menu-structure.js +67 -84
  192. package/dist/helpers/use-runtime-values.d.ts +2 -1
  193. package/dist/helpers/use-runtime-values.js +7 -3
  194. package/dist/icons/cloud-download.d.ts +4 -0
  195. package/dist/icons/cloud-download.js +8 -0
  196. package/dist/icons/remotion-triangle.d.ts +2 -0
  197. package/dist/icons/remotion-triangle.js +7 -0
  198. package/dist/renderEntry.js +1 -1
  199. package/dist/state/composition-list.d.ts +13 -0
  200. package/dist/state/composition-list.js +21 -0
  201. package/dist/state/modals.d.ts +8 -2
  202. package/dist/state/timeline-zoom.d.ts +1 -2
  203. package/dist/state/timeline-zoom.js +9 -5
  204. package/package.json +14 -13
@@ -38,16 +38,18 @@ const jsx_runtime_1 = require("react/jsx-runtime");
38
38
  const react_1 = __importStar(require("react"));
39
39
  const timeline_layout_1 = require("../../helpers/timeline-layout");
40
40
  const MaxTimelineTracks_1 = require("./MaxTimelineTracks");
41
- const TimelineTimeIndicators_1 = require("./TimelineTimeIndicators");
42
41
  const TimelineTrack_1 = require("./TimelineTrack");
42
+ const TimelineVirtualization_1 = require("./TimelineVirtualization");
43
43
  const content = {
44
44
  paddingLeft: timeline_layout_1.TIMELINE_PADDING,
45
45
  paddingRight: timeline_layout_1.TIMELINE_PADDING,
46
+ position: 'relative',
46
47
  };
47
48
  const timelineContent = {
48
49
  minHeight: '100%',
49
50
  };
50
- const TimelineTracksInner = ({ timeline, hasBeenCut }) => {
51
+ const TimelineTracksInner = ({ hasBeenCut }) => {
52
+ const { rows, tracksEnd, virtualItems } = (0, TimelineVirtualization_1.useTimelineVirtualization)();
51
53
  const timelineStyle = (0, react_1.useMemo)(() => {
52
54
  return {
53
55
  ...timelineContent,
@@ -55,7 +57,12 @@ const TimelineTracksInner = ({ timeline, hasBeenCut }) => {
55
57
  };
56
58
  }, []);
57
59
  return (jsx_runtime_1.jsxs("div", { style: timelineStyle, children: [
58
- jsx_runtime_1.jsxs("div", { style: content, children: [
59
- jsx_runtime_1.jsx(TimelineTimeIndicators_1.TimelineTimePadding, {}), timeline.map((track) => (jsx_runtime_1.jsx(TimelineTrack_1.TimelineTrack, { track: track }, track.sequence.id)))] }), hasBeenCut ? jsx_runtime_1.jsx(MaxTimelineTracks_1.MaxTimelineTracksReached, {}) : null] }));
60
+ jsx_runtime_1.jsx("div", { style: { ...content, height: tracksEnd }, children: virtualItems.map((virtualItem) => (jsx_runtime_1.jsx("div", { style: {
61
+ height: virtualItem.size,
62
+ left: timeline_layout_1.TIMELINE_PADDING,
63
+ position: 'absolute',
64
+ right: timeline_layout_1.TIMELINE_PADDING,
65
+ top: virtualItem.start,
66
+ }, children: jsx_runtime_1.jsx(TimelineTrack_1.TimelineTrack, { track: rows[virtualItem.index].track }) }, virtualItem.key))) }), hasBeenCut ? jsx_runtime_1.jsx(MaxTimelineTracks_1.MaxTimelineTracksReached, {}) : null] }));
60
67
  };
61
68
  exports.TimelineTracks = react_1.default.memo(TimelineTracksInner);
@@ -198,7 +198,7 @@ const TimelineTransformOriginField = ({ field, propStatus, effectiveValue, onSav
198
198
  serializeZ,
199
199
  ]);
200
200
  if (percent === null) {
201
- return jsx_runtime_1.jsx(TimelineSchemaField_1.UnsupportedStatus, { label: "unsupported origin" });
201
+ return (jsx_runtime_1.jsx(TimelineSchemaField_1.UnsupportedStatus, { label: "unsupported origin", formattedValue: false }));
202
202
  }
203
203
  return (jsx_runtime_1.jsxs("span", { style: containerStyle, children: [
204
204
  jsx_runtime_1.jsx(InputDragger_1.InputDragger, { type: "number", value: dragX !== null && dragX !== void 0 ? dragX : percent[0], buttonStyle: leftDraggerStyle, style: leftDraggerStyle, status: "ok", small: true, onValueChange: onXChange, onValueChangeEnd: onXChangeEnd, onTextChange: onXTextChange, min: -Infinity, max: Infinity, step: step, formatter: formatter, rightAlign: false, snapToStep: false, dragDecimalPlaces: decimalPlaces, "aria-label": "Transform origin X" }), jsx_runtime_1.jsx("div", { style: { marginLeft: -6, marginRight: -6 } }), jsx_runtime_1.jsx(InputDragger_1.InputDragger, { type: "number", value: dragY !== null && dragY !== void 0 ? dragY : percent[1], buttonStyle: rightDraggerStyle, style: rightDraggerStyle, status: "ok", small: true, onValueChange: onYChange, onValueChangeEnd: onYChangeEnd, onTextChange: onYTextChange, min: -Infinity, max: Infinity, step: step, formatter: formatter, rightAlign: false, snapToStep: false, dragDecimalPlaces: decimalPlaces, "aria-label": "Transform origin Y" }), show3D ? (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
@@ -3,16 +3,16 @@ import type { LoopDisplay } from 'remotion';
3
3
  export declare const TimelineVideoInfo: React.NamedExoticComponent<{
4
4
  readonly src: string;
5
5
  readonly visualizationWidth: number;
6
- readonly naturalWidth: number;
6
+ readonly displayOffsetInFrames: number;
7
+ readonly displayDurationInFrames: number;
7
8
  readonly startMediaFrom: number;
8
9
  readonly mediaFrameAtSequenceZero: number | null;
9
10
  readonly sequenceFrameOffset: number;
10
- readonly durationInFrames: number;
11
11
  readonly playbackRate: number;
12
12
  readonly volume: string | number;
13
13
  readonly doesVolumeChange: boolean;
14
- readonly premountWidth: number;
15
- readonly postmountWidth: number;
14
+ readonly muted: boolean;
15
+ readonly marginLeft: number;
16
16
  readonly loopDisplay: LoopDisplay | undefined;
17
17
  readonly frozenMediaFrame: number | null;
18
18
  readonly extendLastFrame: boolean;
@@ -42,7 +42,6 @@ const colors_1 = require("../../helpers/colors");
42
42
  const timeline_layout_1 = require("../../helpers/timeline-layout");
43
43
  const AudioWaveform_1 = require("../AudioWaveform");
44
44
  const get_timeline_media_start_frame_1 = require("./get-timeline-media-start-frame");
45
- const get_timeline_video_info_widths_1 = require("./get-timeline-video-info-widths");
46
45
  const timeline_video_filmstrip_times_1 = require("./timeline-video-filmstrip-times");
47
46
  const outerStyle = {
48
47
  width: '100%',
@@ -59,8 +58,10 @@ const filmstripContainerStyle = {
59
58
  fontFamily: 'Arial, Helvetica',
60
59
  };
61
60
  const MAX_FROZEN_FRAME_CACHE_DEVIATION = timeline_utils_1.WEBCODECS_TIMESCALE * 0.05;
62
- const TimelineVideoInfoInner = ({ src, visualizationWidth, naturalWidth, startMediaFrom, mediaFrameAtSequenceZero, sequenceFrameOffset, durationInFrames, playbackRate, volume, doesVolumeChange, premountWidth, postmountWidth, loopDisplay, frozenMediaFrame, extendLastFrame, }) => {
61
+ const TimelineVideoInfoSegment = ({ src, visualizationWidth, startMediaFrom, mediaFrameAtSequenceZero, sequenceFrameOffset, durationInFrames, sourceOffsetInFrames, tiledLoop, playbackRate, volume, doesVolumeChange, muted, frozenMediaFrame, extendLastFrame, }) => {
62
+ var _a, _b;
63
63
  const { fps } = (0, remotion_1.useVideoConfig)();
64
+ const resolvedSrc = remotion_1.Internals.usePreload(src);
64
65
  const ref = (0, react_1.useRef)(null);
65
66
  const [error, setError] = (0, react_1.useState)(null);
66
67
  const aspectRatio = (0, react_1.useRef)((0, timeline_utils_1.getAspectRatioFromCache)(src));
@@ -69,15 +70,8 @@ const TimelineVideoInfoInner = ({ src, visualizationWidth, naturalWidth, startMe
69
70
  mediaFrameAtSequenceZero,
70
71
  sequenceFrameOffset,
71
72
  playbackRate,
72
- });
73
- const { mediaVisualizationWidth, mediaNaturalWidth } = (0, react_1.useMemo)(() => {
74
- return (0, get_timeline_video_info_widths_1.getTimelineVideoInfoWidths)({
75
- visualizationWidth,
76
- naturalWidth,
77
- premountWidth,
78
- postmountWidth,
79
- });
80
- }, [naturalWidth, postmountWidth, premountWidth, visualizationWidth]);
73
+ }) +
74
+ sourceOffsetInFrames * playbackRate;
81
75
  // for rendering frames
82
76
  (0, react_1.useEffect)(() => {
83
77
  if (error) {
@@ -88,19 +82,22 @@ const TimelineVideoInfoInner = ({ src, visualizationWidth, naturalWidth, startMe
88
82
  return;
89
83
  }
90
84
  const controller = new AbortController();
85
+ const pixelRatio = window.devicePixelRatio;
91
86
  const canvas = document.createElement('canvas');
92
- canvas.width = mediaVisualizationWidth;
93
- canvas.height = timeline_layout_1.TIMELINE_LAYER_FILMSTRIP_HEIGHT;
87
+ canvas.width = Math.ceil(visualizationWidth * pixelRatio);
88
+ canvas.height = Math.ceil(timeline_layout_1.TIMELINE_LAYER_FILMSTRIP_HEIGHT * pixelRatio);
89
+ canvas.style.width = visualizationWidth + 'px';
90
+ canvas.style.height = timeline_layout_1.TIMELINE_LAYER_FILMSTRIP_HEIGHT + 'px';
94
91
  const ctx = canvas.getContext('2d');
95
92
  if (!ctx) {
96
93
  return;
97
94
  }
98
95
  current.appendChild(canvas);
99
96
  const drawRepeatedFrame = (frame) => {
100
- const thumbnailWidth = Math.max(1, frame.displayWidth / window.devicePixelRatio);
97
+ const thumbnailWidth = Math.max(1, frame.displayWidth);
101
98
  ctx.clearRect(0, 0, canvas.width, canvas.height);
102
99
  for (let x = 0; x < canvas.width; x += thumbnailWidth) {
103
- ctx.drawImage(frame, x, 0, thumbnailWidth, timeline_layout_1.TIMELINE_LAYER_FILMSTRIP_HEIGHT);
100
+ ctx.drawImage(frame, x, 0, thumbnailWidth, canvas.height);
104
101
  }
105
102
  };
106
103
  const getCachedFrozenFrame = (timestamp) => {
@@ -128,7 +125,7 @@ const TimelineVideoInfoInner = ({ src, visualizationWidth, naturalWidth, startMe
128
125
  durationInFrames,
129
126
  playbackRate,
130
127
  fps,
131
- loopDisplay,
128
+ loopDisplay: undefined,
132
129
  frozenMediaFrame,
133
130
  });
134
131
  if (times.type === 'frozen') {
@@ -147,13 +144,12 @@ const TimelineVideoInfoInner = ({ src, visualizationWidth, naturalWidth, startMe
147
144
  timeline_utils_1.aspectRatioCache.set(src, aspectRatio.current);
148
145
  return [times.timestampInSeconds];
149
146
  },
150
- src,
147
+ src: resolvedSrc,
151
148
  onVideoSample: (sample) => {
152
149
  let frame;
153
150
  try {
154
151
  frame = sample.toVideoFrame();
155
- const scale = (timeline_layout_1.TIMELINE_LAYER_FILMSTRIP_HEIGHT / frame.displayHeight) *
156
- window.devicePixelRatio;
152
+ const scale = canvas.height / frame.displayHeight;
157
153
  const transformed = (0, timeline_utils_1.resizeVideoFrame)({
158
154
  frame,
159
155
  scale,
@@ -185,42 +181,44 @@ const TimelineVideoInfoInner = ({ src, visualizationWidth, naturalWidth, startMe
185
181
  current.removeChild(canvas);
186
182
  };
187
183
  }
188
- const loopWidth = (0, timeline_utils_1.getLoopDisplayWidth)({
189
- visualizationWidth: mediaNaturalWidth,
190
- loopDisplay,
191
- });
192
- const shouldRepeatVideo = (0, timeline_utils_1.shouldTileLoopDisplay)(loopDisplay);
193
- const targetCanvas = shouldRepeatVideo
194
- ? document.createElement('canvas')
195
- : canvas;
196
- targetCanvas.width = shouldRepeatVideo
197
- ? Math.max(1, Math.ceil(loopWidth))
184
+ const targetCanvas = tiledLoop ? document.createElement('canvas') : canvas;
185
+ targetCanvas.width = tiledLoop
186
+ ? Math.max(1, Math.ceil(tiledLoop.loopWidth * pixelRatio))
198
187
  : canvas.width;
199
188
  targetCanvas.height = canvas.height;
200
- const targetCtx = shouldRepeatVideo ? targetCanvas.getContext('2d') : ctx;
189
+ const targetCtx = tiledLoop ? targetCanvas.getContext('2d') : ctx;
201
190
  if (!targetCtx) {
202
191
  current.removeChild(canvas);
203
192
  return;
204
193
  }
194
+ // desired-timestamp -> filled-timestamp
195
+ const filledSlots = new Map();
196
+ const { fromSeconds, toSeconds } = times;
197
+ const targetWidth = targetCanvas.width;
205
198
  const repeatTarget = () => {
206
- if (!shouldRepeatVideo) {
199
+ if (!tiledLoop) {
207
200
  return;
208
201
  }
209
202
  const pattern = ctx.createPattern(targetCanvas, 'repeat-x');
210
203
  if (!pattern) {
211
204
  return;
212
205
  }
213
- pattern.setTransform(new DOMMatrix().scaleSelf(loopWidth / targetCanvas.width, 1));
206
+ const phase = (tiledLoop.displayOffsetInFrames %
207
+ tiledLoop.loopDisplay.durationInFrames) *
208
+ ((tiledLoop.loopWidth * pixelRatio) /
209
+ tiledLoop.loopDisplay.durationInFrames);
210
+ pattern.setTransform(new DOMMatrix([
211
+ (tiledLoop.loopWidth * pixelRatio) / targetCanvas.width,
212
+ 0,
213
+ 0,
214
+ 1,
215
+ -phase,
216
+ 0,
217
+ ]));
214
218
  ctx.clearRect(0, 0, canvas.width, canvas.height);
215
219
  ctx.fillStyle = pattern;
216
220
  ctx.fillRect(0, 0, canvas.width, canvas.height);
217
221
  };
218
- // desired-timestamp -> filled-timestamp
219
- const filledSlots = new Map();
220
- const { fromSeconds, toSeconds } = times;
221
- const targetWidth = shouldRepeatVideo
222
- ? targetCanvas.width
223
- : mediaNaturalWidth;
224
222
  if (aspectRatio.current !== null) {
225
223
  (0, timeline_utils_1.ensureSlots)({
226
224
  filledSlots,
@@ -228,7 +226,7 @@ const TimelineVideoInfoInner = ({ src, visualizationWidth, naturalWidth, startMe
228
226
  fromSeconds,
229
227
  toSeconds,
230
228
  aspectRatio: aspectRatio.current,
231
- frameHeight: timeline_layout_1.TIMELINE_LAYER_FILMSTRIP_HEIGHT,
229
+ frameHeight: canvas.height,
232
230
  });
233
231
  (0, timeline_utils_1.fillWithCachedFrames)({
234
232
  ctx: targetCtx,
@@ -237,11 +235,11 @@ const TimelineVideoInfoInner = ({ src, visualizationWidth, naturalWidth, startMe
237
235
  src,
238
236
  segmentDuration: toSeconds - fromSeconds,
239
237
  fromSeconds,
240
- devicePixelRatio: window.devicePixelRatio,
241
- frameHeight: timeline_layout_1.TIMELINE_LAYER_FILMSTRIP_HEIGHT,
238
+ devicePixelRatio: 1,
239
+ frameHeight: canvas.height,
242
240
  });
243
241
  repeatTarget();
244
- const unfilled = Array.from(filledSlots.keys()).filter((timestamp) => !filledSlots.get(timestamp));
242
+ const unfilled = Array.from(filledSlots.keys()).filter((timestamp) => filledSlots.get(timestamp) === undefined);
245
243
  // Don't extract frames if all slots are filled
246
244
  if (unfilled.length === 0) {
247
245
  return () => {
@@ -260,17 +258,29 @@ const TimelineVideoInfoInner = ({ src, visualizationWidth, naturalWidth, startMe
260
258
  toSeconds,
261
259
  naturalWidth: targetWidth,
262
260
  aspectRatio: aspectRatio.current,
263
- frameHeight: timeline_layout_1.TIMELINE_LAYER_FILMSTRIP_HEIGHT,
261
+ frameHeight: canvas.height,
262
+ });
263
+ (0, timeline_utils_1.fillWithCachedFrames)({
264
+ ctx: targetCtx,
265
+ naturalWidth: targetWidth,
266
+ filledSlots,
267
+ src,
268
+ segmentDuration: toSeconds - fromSeconds,
269
+ fromSeconds,
270
+ devicePixelRatio: 1,
271
+ frameHeight: canvas.height,
264
272
  });
265
- return Array.from(filledSlots.keys()).map((timestamp) => timestamp / timeline_utils_1.WEBCODECS_TIMESCALE);
273
+ repeatTarget();
274
+ return Array.from(filledSlots.keys())
275
+ .filter((timestamp) => filledSlots.get(timestamp) === undefined)
276
+ .map((timestamp) => timestamp / timeline_utils_1.WEBCODECS_TIMESCALE);
266
277
  },
267
- src,
278
+ src: resolvedSrc,
268
279
  onVideoSample: (sample) => {
269
280
  let frame;
270
281
  try {
271
282
  frame = sample.toVideoFrame();
272
- const scale = (timeline_layout_1.TIMELINE_LAYER_FILMSTRIP_HEIGHT / frame.displayHeight) *
273
- window.devicePixelRatio;
283
+ const scale = canvas.height / frame.displayHeight;
274
284
  const transformed = (0, timeline_utils_1.resizeVideoFrame)({
275
285
  frame,
276
286
  scale,
@@ -290,7 +300,7 @@ const TimelineVideoInfoInner = ({ src, visualizationWidth, naturalWidth, startMe
290
300
  toSeconds,
291
301
  naturalWidth: targetWidth,
292
302
  aspectRatio: aspectRatio.current,
293
- frameHeight: timeline_layout_1.TIMELINE_LAYER_FILMSTRIP_HEIGHT,
303
+ frameHeight: canvas.height,
294
304
  });
295
305
  (0, timeline_utils_1.fillFrameWhereItFits)({
296
306
  ctx: targetCtx,
@@ -299,8 +309,8 @@ const TimelineVideoInfoInner = ({ src, visualizationWidth, naturalWidth, startMe
299
309
  frame: transformed,
300
310
  segmentDuration: toSeconds - fromSeconds,
301
311
  fromSeconds,
302
- devicePixelRatio: window.devicePixelRatio,
303
- frameHeight: timeline_layout_1.TIMELINE_LAYER_FILMSTRIP_HEIGHT,
312
+ devicePixelRatio: 1,
313
+ frameHeight: canvas.height,
304
314
  });
305
315
  repeatTarget();
306
316
  }
@@ -327,8 +337,8 @@ const TimelineVideoInfoInner = ({ src, visualizationWidth, naturalWidth, startMe
327
337
  src,
328
338
  segmentDuration: toSeconds - fromSeconds,
329
339
  fromSeconds,
330
- devicePixelRatio: window.devicePixelRatio,
331
- frameHeight: timeline_layout_1.TIMELINE_LAYER_FILMSTRIP_HEIGHT,
340
+ devicePixelRatio: 1,
341
+ frameHeight: canvas.height,
332
342
  });
333
343
  repeatTarget();
334
344
  })
@@ -345,30 +355,112 @@ const TimelineVideoInfoInner = ({ src, visualizationWidth, naturalWidth, startMe
345
355
  extendLastFrame,
346
356
  fps,
347
357
  frozenMediaFrame,
348
- loopDisplay,
349
- mediaNaturalWidth,
350
- mediaVisualizationWidth,
351
358
  mediaStartFrame,
352
359
  playbackRate,
360
+ resolvedSrc,
353
361
  src,
362
+ tiledLoop,
363
+ visualizationWidth,
354
364
  ]);
355
- const audioWidth = mediaVisualizationWidth;
365
+ const audioWidth = visualizationWidth;
356
366
  const filmstripStyle = (0, react_1.useMemo)(() => {
357
367
  return {
358
368
  ...filmstripContainerStyle,
359
- width: mediaVisualizationWidth,
360
- marginLeft: premountWidth,
369
+ width: visualizationWidth,
361
370
  };
362
- }, [mediaVisualizationWidth, premountWidth]);
371
+ }, [visualizationWidth]);
363
372
  const audioStyle = (0, react_1.useMemo)(() => {
364
373
  return {
365
374
  width: audioWidth,
366
375
  position: 'relative',
367
- marginLeft: premountWidth,
368
376
  };
369
- }, [audioWidth, premountWidth]);
370
- return (jsx_runtime_1.jsxs("div", { style: outerStyle, children: [
371
- jsx_runtime_1.jsx("div", { ref: ref, style: filmstripStyle }), jsx_runtime_1.jsx("div", { style: audioStyle, children: jsx_runtime_1.jsx(AudioWaveform_1.AudioWaveform, { src: src, height: timeline_layout_1.TIMELINE_VIDEO_INFO_WAVEFORM_HEIGHT, visualizationWidth: audioWidth, startFrom: mediaStartFrame, durationInFrames: durationInFrames, volume: volume, doesVolumeChange: doesVolumeChange, playbackRate: playbackRate, loopDisplay: loopDisplay }) })
377
+ }, [audioWidth]);
378
+ const segmentStyle = (0, react_1.useMemo)(() => {
379
+ return {
380
+ ...outerStyle,
381
+ width: visualizationWidth,
382
+ flexShrink: 0,
383
+ };
384
+ }, [visualizationWidth]);
385
+ return (jsx_runtime_1.jsxs("div", { style: segmentStyle, children: [
386
+ jsx_runtime_1.jsx("div", { ref: ref, style: filmstripStyle }), jsx_runtime_1.jsx("div", { style: audioStyle, children: jsx_runtime_1.jsx(AudioWaveform_1.AudioWaveform, { src: src, height: timeline_layout_1.TIMELINE_VIDEO_INFO_WAVEFORM_HEIGHT, visualizationWidth: audioWidth, startFrom: mediaStartFrame, durationInFrames: durationInFrames, displayOffsetInFrames: (_a = tiledLoop === null || tiledLoop === void 0 ? void 0 : tiledLoop.displayOffsetInFrames) !== null && _a !== void 0 ? _a : 0, displayDurationInFrames: (_b = tiledLoop === null || tiledLoop === void 0 ? void 0 : tiledLoop.displayDurationInFrames) !== null && _b !== void 0 ? _b : durationInFrames, volume: volume, doesVolumeChange: doesVolumeChange, muted: muted, playbackRate: playbackRate, loopDisplay: tiledLoop === null || tiledLoop === void 0 ? void 0 : tiledLoop.loopDisplay }) })
372
387
  ] }));
373
388
  };
389
+ const getVisibleSegments = ({ displayDurationInFrames, displayOffsetInFrames, loopDisplay, }) => {
390
+ if (!loopDisplay ||
391
+ loopDisplay.numberOfTimes <= 1 ||
392
+ loopDisplay.durationInFrames <= 0) {
393
+ return [
394
+ {
395
+ key: 'single',
396
+ displayOffsetInFrames,
397
+ durationInFrames: displayDurationInFrames,
398
+ sourceOffsetInFrames: displayOffsetInFrames,
399
+ },
400
+ ];
401
+ }
402
+ return (0, timeline_utils_1.getLoopDisplaySegments)({
403
+ displayDurationInFrames,
404
+ displayOffsetInFrames,
405
+ loopDurationInFrames: loopDisplay.durationInFrames,
406
+ }).map((segment) => {
407
+ return {
408
+ key: `loop-${segment.loopIndex}`,
409
+ displayOffsetInFrames: segment.absoluteOffsetInFrames,
410
+ durationInFrames: segment.durationInFrames,
411
+ sourceOffsetInFrames: segment.loopOffsetInFrames,
412
+ };
413
+ });
414
+ };
415
+ const TimelineVideoInfoInner = ({ src, visualizationWidth, displayOffsetInFrames, displayDurationInFrames, startMediaFrom, mediaFrameAtSequenceZero, sequenceFrameOffset, playbackRate, volume, doesVolumeChange, muted, marginLeft, loopDisplay, frozenMediaFrame, extendLastFrame, }) => {
416
+ const pixelsPerFrame = visualizationWidth / displayDurationInFrames;
417
+ const loopWidth = loopDisplay
418
+ ? loopDisplay.durationInFrames * pixelsPerFrame
419
+ : null;
420
+ const shouldTileLoop = loopDisplay !== undefined &&
421
+ loopWidth !== null &&
422
+ loopWidth <= visualizationWidth;
423
+ const tiledLoop = (0, react_1.useMemo)(() => {
424
+ if (!shouldTileLoop || !loopDisplay || loopWidth === null) {
425
+ return null;
426
+ }
427
+ return {
428
+ displayDurationInFrames,
429
+ displayOffsetInFrames,
430
+ loopDisplay,
431
+ loopWidth,
432
+ };
433
+ }, [
434
+ displayDurationInFrames,
435
+ displayOffsetInFrames,
436
+ loopDisplay,
437
+ loopWidth,
438
+ shouldTileLoop,
439
+ ]);
440
+ const segments = (0, react_1.useMemo)(() => {
441
+ if (shouldTileLoop) {
442
+ return [];
443
+ }
444
+ return getVisibleSegments({
445
+ displayDurationInFrames,
446
+ displayOffsetInFrames,
447
+ loopDisplay,
448
+ });
449
+ }, [
450
+ displayDurationInFrames,
451
+ displayOffsetInFrames,
452
+ loopDisplay,
453
+ shouldTileLoop,
454
+ ]);
455
+ const getSegmentVolume = (segmentDisplayOffsetInFrames, segmentDurationInFrames) => {
456
+ if (typeof volume === 'number') {
457
+ return volume;
458
+ }
459
+ const values = volume.split(',');
460
+ return values
461
+ .slice(Math.max(0, Math.floor(segmentDisplayOffsetInFrames)), Math.min(values.length, Math.ceil(segmentDisplayOffsetInFrames + segmentDurationInFrames)))
462
+ .join(',');
463
+ };
464
+ return (jsx_runtime_1.jsx("div", { style: { display: 'flex', marginLeft, height: '100%' }, children: tiledLoop ? (jsx_runtime_1.jsx(TimelineVideoInfoSegment, { src: src, visualizationWidth: visualizationWidth, startMediaFrom: startMediaFrom, mediaFrameAtSequenceZero: mediaFrameAtSequenceZero, sequenceFrameOffset: sequenceFrameOffset, durationInFrames: tiledLoop.loopDisplay.durationInFrames, sourceOffsetInFrames: 0, tiledLoop: tiledLoop, playbackRate: playbackRate, volume: volume, doesVolumeChange: doesVolumeChange, muted: muted, frozenMediaFrame: frozenMediaFrame, extendLastFrame: extendLastFrame })) : (segments.map((segment) => (jsx_runtime_1.jsx(TimelineVideoInfoSegment, { src: src, visualizationWidth: segment.durationInFrames * pixelsPerFrame, startMediaFrom: startMediaFrom, mediaFrameAtSequenceZero: mediaFrameAtSequenceZero, sequenceFrameOffset: sequenceFrameOffset, durationInFrames: segment.durationInFrames, sourceOffsetInFrames: segment.sourceOffsetInFrames, tiledLoop: null, playbackRate: playbackRate, volume: getSegmentVolume(segment.sourceOffsetInFrames, segment.durationInFrames), doesVolumeChange: doesVolumeChange, muted: muted, frozenMediaFrame: frozenMediaFrame, extendLastFrame: extendLastFrame }, segment.key)))) }));
465
+ };
374
466
  exports.TimelineVideoInfo = react_1.default.memo(TimelineVideoInfoInner);
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ type TimelineRenderWindow = {
3
+ readonly left: number;
4
+ readonly width: number;
5
+ };
6
+ export declare const TimelineViewportContext: React.Context<TimelineRenderWindow | null>;
7
+ export declare const getTimelineRenderWindow: (scrollable: HTMLDivElement) => TimelineRenderWindow;
8
+ export declare const TimelineViewportProvider: React.FC<{
9
+ readonly children: React.ReactNode;
10
+ readonly scrollable: React.RefObject<HTMLDivElement | null>;
11
+ }>;
12
+ export {};
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TimelineViewportProvider = exports.getTimelineRenderWindow = exports.TimelineViewportContext = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ exports.TimelineViewportContext = (0, react_1.createContext)(null);
7
+ const getTimelineRenderWindow = (scrollable) => {
8
+ const viewportWidth = scrollable.clientWidth;
9
+ if (viewportWidth === 0) {
10
+ return { left: 0, width: 0 };
11
+ }
12
+ // Move the render window half a viewport before the visible viewport reaches
13
+ // its right edge. This keeps at least half a viewport rendered ahead while
14
+ // retaining the three-viewport-wide window.
15
+ const currentSection = Math.floor((scrollable.scrollLeft + viewportWidth / 2) / viewportWidth);
16
+ const left = Math.max(0, (currentSection - 1) * viewportWidth);
17
+ return {
18
+ left,
19
+ width: viewportWidth * 3,
20
+ };
21
+ };
22
+ exports.getTimelineRenderWindow = getTimelineRenderWindow;
23
+ const TimelineViewportProvider = ({ children, scrollable }) => {
24
+ const [renderWindow, setRenderWindow] = (0, react_1.useState)({
25
+ left: 0,
26
+ width: window.innerWidth * 3,
27
+ });
28
+ const update = (0, react_1.useCallback)(() => {
29
+ const element = scrollable.current;
30
+ if (!element) {
31
+ return;
32
+ }
33
+ const next = (0, exports.getTimelineRenderWindow)(element);
34
+ setRenderWindow((previous) => {
35
+ if (previous.left === next.left && previous.width === next.width) {
36
+ return previous;
37
+ }
38
+ return next;
39
+ });
40
+ }, [scrollable]);
41
+ (0, react_1.useLayoutEffect)(() => {
42
+ let element = null;
43
+ let resizeObserver = null;
44
+ const animationFrame = requestAnimationFrame(() => {
45
+ element = scrollable.current;
46
+ if (!element) {
47
+ return;
48
+ }
49
+ resizeObserver = new ResizeObserver(update);
50
+ resizeObserver.observe(element);
51
+ update();
52
+ element.addEventListener('scroll', update);
53
+ });
54
+ return () => {
55
+ cancelAnimationFrame(animationFrame);
56
+ resizeObserver === null || resizeObserver === void 0 ? void 0 : resizeObserver.disconnect();
57
+ element === null || element === void 0 ? void 0 : element.removeEventListener('scroll', update);
58
+ };
59
+ }, [scrollable, update]);
60
+ return (jsx_runtime_1.jsx(exports.TimelineViewportContext.Provider, { value: renderWindow, children: children }));
61
+ };
62
+ exports.TimelineViewportProvider = TimelineViewportProvider;
@@ -0,0 +1,22 @@
1
+ import { type VirtualItem } from '@tanstack/react-virtual';
2
+ import React from 'react';
3
+ import type { TimelineTrackData } from '../../helpers/get-timeline-sequence-sort-key';
4
+ export type TimelineVirtualRow = {
5
+ readonly afterDropLineOffset: number;
6
+ readonly siblingIndex: number;
7
+ readonly track: TimelineTrackData;
8
+ };
9
+ type TimelineVirtualizationContextValue = {
10
+ readonly rows: readonly TimelineVirtualRow[];
11
+ readonly totalSize: number;
12
+ readonly tracksEnd: number;
13
+ readonly virtualItems: readonly VirtualItem[];
14
+ };
15
+ export declare const TimelineVirtualizationProvider: React.FC<{
16
+ readonly children: React.ReactNode;
17
+ readonly hasBeenCut: boolean;
18
+ readonly isStill: boolean;
19
+ readonly timeline: readonly TimelineTrackData[];
20
+ }>;
21
+ export declare const useTimelineVirtualization: () => TimelineVirtualizationContextValue;
22
+ export {};