@remotion/studio 4.0.525 → 4.0.527

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 (195) hide show
  1. package/dist/api/focus-default-props-path.js +7 -1
  2. package/dist/api/restart-studio.d.ts +1 -1
  3. package/dist/api/restart-studio.js +1 -1
  4. package/dist/components/AgentPrompt.d.ts +6 -0
  5. package/dist/components/AgentPrompt.js +107 -0
  6. package/dist/components/AssetFileIcon.js +2 -0
  7. package/dist/components/AssetSelectorItem.d.ts +1 -23
  8. package/dist/components/AssetSelectorItem.js +10 -200
  9. package/dist/components/Canvas.js +109 -44
  10. package/dist/components/CanvasCaptureDropHandler.js +63 -0
  11. package/dist/components/CanvasOrLoading.js +10 -2
  12. package/dist/components/CaptionInspector.js +9 -3
  13. package/dist/{error-overlay/remotion-overlay → components}/CodeFrame.d.ts +2 -0
  14. package/dist/components/CodeFrame.js +77 -0
  15. package/dist/components/CompositionSelectorItem.js +7 -17
  16. package/dist/components/CurrentAsset.js +6 -4
  17. package/dist/components/ElementInstallConfirmation.js +69 -38
  18. package/dist/components/FilePreview.js +4 -0
  19. package/dist/components/FixComputedValueModal.js +64 -96
  20. package/dist/components/GenerateWithAgentModal.d.ts +9 -0
  21. package/dist/components/GenerateWithAgentModal.js +39 -0
  22. package/dist/components/InitialCompositionLoader.js +1 -1
  23. package/dist/components/InlineDropdown.js +26 -2
  24. package/dist/components/InlineEditableTitle.js +3 -0
  25. package/dist/components/InspectorOpenInEditor.js +8 -4
  26. package/dist/components/InspectorPanel/CollapsibleInspectorSection.d.ts +7 -0
  27. package/dist/components/InspectorPanel/CollapsibleInspectorSection.js +14 -0
  28. package/dist/components/InspectorPanel/CollapsibleInspectorSectionHeader.d.ts +1 -1
  29. package/dist/components/InspectorPanel/CollapsibleInspectorSectionHeader.js +20 -8
  30. package/dist/components/InspectorPanel/CompositionInspector.js +32 -15
  31. package/dist/components/InspectorPanel/CompositionMetadata.js +7 -0
  32. package/dist/components/InspectorPanel/ConnectedCompositionsSection.js +6 -12
  33. package/dist/components/InspectorPanel/ElementLibraryButton.js +2 -0
  34. package/dist/components/InspectorPanel/GuideInspector.js +2 -3
  35. package/dist/components/InspectorPanel/KeyframeSettings.js +5 -6
  36. package/dist/components/InspectorPanel/MultiSequenceInspector.js +34 -2
  37. package/dist/components/InspectorPanel/SequenceSelectionInspector.js +45 -4
  38. package/dist/components/InspectorPanel/common.d.ts +4 -0
  39. package/dist/components/InspectorPanel/common.js +15 -2
  40. package/dist/components/InspectorPanel/styles.d.ts +0 -3
  41. package/dist/components/InspectorPanel/styles.js +3 -27
  42. package/dist/components/InspectorPanel/use-composition-actions.d.ts +2 -0
  43. package/dist/components/InspectorPanel/use-composition-actions.js +16 -0
  44. package/dist/components/InspectorPanel/use-inspector-section-expanded.d.ts +1 -0
  45. package/dist/components/InspectorPanel/use-inspector-section-expanded.js +21 -0
  46. package/dist/components/InspectorSequenceSection.js +44 -35
  47. package/dist/components/InspectorSourceLocation.js +4 -0
  48. package/dist/components/KeyboardShortcutsSettings.js +12 -4
  49. package/dist/components/LutPreview.d.ts +4 -0
  50. package/dist/components/LutPreview.js +192 -0
  51. package/dist/components/MenuBuildIndicator.js +1 -0
  52. package/dist/components/MenuCompositionName.js +4 -9
  53. package/dist/components/Modals.js +3 -6
  54. package/dist/components/ModelsSettings.js +15 -4
  55. package/dist/components/NewComposition/CancelButton.js +1 -1
  56. package/dist/components/Notifications/NotificationCenter.d.ts +0 -1
  57. package/dist/components/Notifications/NotificationCenter.js +1 -22
  58. package/dist/components/Preview.js +3 -0
  59. package/dist/components/QuickSwitcher/ExplorerQuickSwitcherTrigger.js +1 -0
  60. package/dist/components/QuickSwitcher/QuickSwitcherContent.js +1 -0
  61. package/dist/components/QuickSwitcher/QuickSwitcherResult.js +4 -9
  62. package/dist/components/RadioButton.js +1 -0
  63. package/dist/components/RenderModal/DataEditor.js +4 -1
  64. package/dist/components/RenderModal/SchemaEditor/scroll-to-default-props-path.d.ts +1 -0
  65. package/dist/components/RenderModal/SchemaEditor/scroll-to-default-props-path.js +2 -1
  66. package/dist/components/RenderModal/WebRenderModal.js +10 -2
  67. package/dist/components/RenderQueue/CaptionQueueItem.js +5 -2
  68. package/dist/components/RenderQueue/CaptionQueueProcessor.js +35 -8
  69. package/dist/components/RenderQueue/VideoMattingQueueItem.js +1 -0
  70. package/dist/components/RenderQueue/caption-job-types.d.ts +6 -0
  71. package/dist/components/RenderQueue/index.js +1 -0
  72. package/dist/components/RendersTab.js +4 -0
  73. package/dist/components/SelectedOutlineElement.js +6 -0
  74. package/dist/components/SelectedOutlineSnapIndicators.d.ts +6 -0
  75. package/dist/components/SelectedOutlineSnapIndicators.js +16 -12
  76. package/dist/components/SequencePropsSubscriptionProvider.d.ts +3 -0
  77. package/dist/components/SequencePropsSubscriptionProvider.js +8 -6
  78. package/dist/components/SettingsButton.js +11 -2
  79. package/dist/components/SettingsModal.d.ts +1 -2
  80. package/dist/components/SettingsModal.js +24 -6
  81. package/dist/components/SettingsModalFooter.js +17 -3
  82. package/dist/components/SkillsSettings.d.ts +5 -0
  83. package/dist/components/SkillsSettings.js +29 -26
  84. package/dist/components/Timeline/Timeline.js +10 -11
  85. package/dist/components/Timeline/TimelineAssetField.js +81 -51
  86. package/dist/components/Timeline/TimelineDeleteKeybindings.js +6 -9
  87. package/dist/components/Timeline/TimelineEffectItem.js +1 -0
  88. package/dist/components/Timeline/TimelineExpandArrowButton.js +1 -0
  89. package/dist/components/Timeline/TimelineExpandedRow.js +1 -0
  90. package/dist/components/Timeline/TimelineFieldLabel.js +1 -0
  91. package/dist/components/Timeline/TimelineHeightContainer.js +11 -1
  92. package/dist/components/Timeline/TimelineKeyframeControls.js +16 -1
  93. package/dist/components/Timeline/TimelineLayerChildren.d.ts +3 -3
  94. package/dist/components/Timeline/TimelineLayerChildren.js +11 -9
  95. package/dist/components/Timeline/TimelineLayerEye.d.ts +1 -1
  96. package/dist/components/Timeline/TimelineLayerEye.js +6 -3
  97. package/dist/components/Timeline/TimelineList.js +9 -3
  98. package/dist/components/Timeline/TimelineSchemaField.js +2 -6
  99. package/dist/components/Timeline/TimelineSelection.d.ts +14 -3
  100. package/dist/components/Timeline/TimelineSelection.js +91 -49
  101. package/dist/components/Timeline/TimelineSequence.js +28 -2
  102. package/dist/components/Timeline/TimelineSequenceFrame.js +1 -0
  103. package/dist/components/Timeline/TimelineSequenceItem.d.ts +2 -0
  104. package/dist/components/Timeline/TimelineSequenceItem.js +45 -13
  105. package/dist/components/Timeline/TimelineViewport.d.ts +1 -0
  106. package/dist/components/Timeline/TimelineViewport.js +10 -1
  107. package/dist/components/Timeline/TimelineVirtualization.d.ts +3 -3
  108. package/dist/components/Timeline/delete-selected-timeline-item.js +2 -2
  109. package/dist/components/Timeline/get-sequence-context-menu-items.d.ts +6 -2
  110. package/dist/components/Timeline/get-sequence-context-menu-items.js +143 -46
  111. package/dist/components/Timeline/normalize-font-weight-for-keyframe.d.ts +1 -0
  112. package/dist/components/Timeline/normalize-font-weight-for-keyframe.js +19 -0
  113. package/dist/components/Timeline/split-selected-timeline-item.d.ts +13 -10
  114. package/dist/components/Timeline/split-selected-timeline-item.js +58 -35
  115. package/dist/components/Timeline/timeline-display-groups.d.ts +9 -0
  116. package/dist/components/Timeline/timeline-display-groups.js +66 -0
  117. package/dist/components/Timeline/use-timeline-asset-drop.js +21 -21
  118. package/dist/components/Timeline/use-timeline-expanded-tree.d.ts +2 -1
  119. package/dist/components/Timeline/use-timeline-expanded-tree.js +1 -2
  120. package/dist/components/TimelineCombobox.js +1 -0
  121. package/dist/components/Transcription/TranscriptionModal.js +16 -11
  122. package/dist/components/Transcription/TranscriptionModalWithOptionalWhisper.js +1 -1
  123. package/dist/components/UpdateStatusContext.d.ts +2 -2
  124. package/dist/components/UpdateStatusContext.js +6 -7
  125. package/dist/components/UpdatesSettings.js +6 -6
  126. package/dist/components/WebMcp.js +41 -41
  127. package/dist/components/asset-context-menu.d.ts +32 -0
  128. package/dist/components/asset-context-menu.js +242 -0
  129. package/dist/components/composition-drop-preview.d.ts +9 -2
  130. package/dist/components/composition-drop-preview.js +7 -4
  131. package/dist/components/composition-menu-items.d.ts +4 -6
  132. package/dist/components/composition-menu-items.js +191 -75
  133. package/dist/components/delete-jsx-nodes-api.d.ts +2 -0
  134. package/dist/components/{delete-jsx-node-api.js → delete-jsx-nodes-api.js} +5 -5
  135. package/dist/components/get-open-in-menu-items.d.ts +6 -5
  136. package/dist/components/get-open-in-menu-items.js +22 -16
  137. package/dist/components/keyboard-shortcuts.d.ts +4 -4
  138. package/dist/components/keyboard-shortcuts.js +3 -1
  139. package/dist/components/public-output-name.d.ts +1 -0
  140. package/dist/components/public-output-name.js +7 -3
  141. package/dist/components/selected-outline-order.js +18 -12
  142. package/dist/components/use-open-in-menu-apps.d.ts +14 -0
  143. package/dist/components/use-open-in-menu-apps.js +33 -0
  144. package/dist/error-overlay/remotion-overlay/ErrorDisplay.js +2 -2
  145. package/dist/error-overlay/remotion-overlay/OpenInEditor.d.ts +1 -0
  146. package/dist/error-overlay/remotion-overlay/OpenInEditor.js +3 -3
  147. package/dist/error-overlay/remotion-overlay/StackFrame.d.ts +5 -1
  148. package/dist/error-overlay/remotion-overlay/StackFrame.js +26 -19
  149. package/dist/esm/{CaptionQueueProcessor-k0yrm3dj.mjs → CaptionQueueProcessor-npa6szn1.mjs} +39 -8
  150. package/dist/esm/{LazyModels-gfmpcy4h.mjs → LazyModels-nh3c8h9p.mjs} +4 -3
  151. package/dist/esm/{LazyModels-m1rf06wh.mjs → LazyModels-rr5v8h5y.mjs} +4 -3
  152. package/dist/esm/{TranscriptionModal-8pvrehg4.mjs → TranscriptionModal-79m5wgcj.mjs} +25 -17
  153. package/dist/esm/{VideoMattingModal-fefbzgka.mjs → VideoMattingModal-31pew0xj.mjs} +9 -6
  154. package/dist/esm/index-21hxsfaa.mjs +239 -0
  155. package/dist/esm/{index-s848rffx.mjs → index-4zen9prq.mjs} +5438 -4686
  156. package/dist/esm/index-5t2dwxsk.mjs +530 -0
  157. package/dist/esm/{index-vsxrxfpj.mjs → index-614tdz2q.mjs} +1 -1
  158. package/dist/esm/{index-0x13f28h.mjs → index-9xxg6218.mjs} +20 -541
  159. package/dist/esm/{index-hbezgbbq.mjs → index-ac5550e7.mjs} +1 -1
  160. package/dist/esm/{index-41kqns5c.mjs → index-e855w59j.mjs} +15 -92
  161. package/dist/esm/{index-tzxb8808.mjs → index-n5gmcvhz.mjs} +8 -8
  162. package/dist/esm/{index-ggqpre9v.mjs → index-ppspzgn9.mjs} +4963 -4178
  163. package/dist/esm/{index-jrw61pkj.mjs → index-r0ejh5sy.mjs} +1 -1
  164. package/dist/esm/index-r1gr3j3a.mjs +80 -0
  165. package/dist/esm/{index-mww32xc8.mjs → index-s7qa3xbw.mjs} +6 -4
  166. package/dist/esm/{index-1089795y.mjs → index-vxwg66ar.mjs} +7 -22
  167. package/dist/esm/index.mjs +13 -6
  168. package/dist/esm/internals.mjs +10 -7
  169. package/dist/esm/previewEntry.mjs +10 -7
  170. package/dist/esm/{resolve-composition-component-location-bt2d6qxq.mjs → resolve-composition-component-location-qrp05g62.mjs} +1 -1
  171. package/dist/helpers/client-id.d.ts +1 -0
  172. package/dist/helpers/client-id.js +8 -4
  173. package/dist/helpers/copy-text.js +1 -16
  174. package/dist/helpers/cube-lut.d.ts +12 -0
  175. package/dist/helpers/cube-lut.js +160 -0
  176. package/dist/helpers/get-preview-file-type.d.ts +1 -1
  177. package/dist/helpers/get-preview-file-type.js +3 -0
  178. package/dist/helpers/hoverable.js +2 -1
  179. package/dist/helpers/open-in-editor.d.ts +5 -2
  180. package/dist/helpers/open-in-editor.js +13 -5
  181. package/dist/helpers/settings-tab-availability.d.ts +14 -0
  182. package/dist/helpers/settings-tab-availability.js +35 -0
  183. package/dist/helpers/studio-runtime-config.d.ts +1 -1
  184. package/dist/helpers/use-keybinding.d.ts +1 -1
  185. package/dist/helpers/use-keyboard-shortcut-label.d.ts +2 -2
  186. package/dist/helpers/use-menu-structure.js +31 -21
  187. package/dist/icons/lut.d.ts +4 -0
  188. package/dist/icons/lut.js +6 -0
  189. package/dist/icons/sparkles.d.ts +4 -0
  190. package/dist/icons/sparkles.js +6 -0
  191. package/dist/state/modals.d.ts +17 -2
  192. package/dist/state/timeline-zoom.js +4 -0
  193. package/package.json +19 -19
  194. package/dist/components/delete-jsx-node-api.d.ts +0 -2
  195. package/dist/error-overlay/remotion-overlay/CodeFrame.js +0 -62
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SequencePropsSubscriptionProvider = exports.getReadOnlyOverrideIdToNodePathMappings = void 0;
3
+ exports.SequencePropsSubscriptionProvider = exports.getReadOnlyOverrideIdToNodePathMappings = exports.OverrideIdToNodePathMappingsRefContext = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const remotion_1 = require("remotion");
7
+ exports.OverrideIdToNodePathMappingsRefContext = (0, react_1.createContext)({ current: {} });
7
8
  const getReadOnlyOverrideIdToNodePathMappings = (sequences) => {
8
9
  return Object.fromEntries(sequences.flatMap((sequence) => {
9
10
  var _a;
@@ -32,6 +33,9 @@ const SequencePropsSubscriptionProvider = ({ children }) => {
32
33
  const readOnlyOverrideToNodePathMap = (0, react_1.useMemo)(() => window.remotion_isReadOnlyStudio
33
34
  ? (0, exports.getReadOnlyOverrideIdToNodePathMappings)(sequences)
34
35
  : null, [sequences]);
36
+ const overrideIdToNodePathMappings = readOnlyOverrideToNodePathMap !== null && readOnlyOverrideToNodePathMap !== void 0 ? readOnlyOverrideToNodePathMap : overrideToNodePathMap;
37
+ const overrideIdToNodePathMappingsRef = (0, react_1.useRef)(overrideIdToNodePathMappings);
38
+ overrideIdToNodePathMappingsRef.current = overrideIdToNodePathMappings;
35
39
  const setOverrideIdToNodePath = (0, react_1.useCallback)((overrideId, state) => {
36
40
  setOverrideIdToNodePathMap((prev) => {
37
41
  const existing = prev[overrideId];
@@ -50,13 +54,11 @@ const SequencePropsSubscriptionProvider = ({ children }) => {
50
54
  });
51
55
  }, []);
52
56
  const getters = (0, react_1.useMemo)(() => {
53
- return {
54
- overrideIdToNodePathMappings: readOnlyOverrideToNodePathMap !== null && readOnlyOverrideToNodePathMap !== void 0 ? readOnlyOverrideToNodePathMap : overrideToNodePathMap,
55
- };
56
- }, [overrideToNodePathMap, readOnlyOverrideToNodePathMap]);
57
+ return { overrideIdToNodePathMappings };
58
+ }, [overrideIdToNodePathMappings]);
57
59
  const setters = (0, react_1.useMemo)(() => {
58
60
  return { setOverrideIdToNodePath };
59
61
  }, [setOverrideIdToNodePath]);
60
- return (jsx_runtime_1.jsx(remotion_1.Internals.OverrideIdsToNodePathsGettersContext.Provider, { value: getters, children: jsx_runtime_1.jsx(remotion_1.Internals.OverrideIdsToNodePathsSettersContext.Provider, { value: setters, children: children }) }));
62
+ return (jsx_runtime_1.jsx(exports.OverrideIdToNodePathMappingsRefContext.Provider, { value: overrideIdToNodePathMappingsRef, children: jsx_runtime_1.jsx(remotion_1.Internals.OverrideIdsToNodePathsGettersContext.Provider, { value: getters, children: jsx_runtime_1.jsx(remotion_1.Internals.OverrideIdsToNodePathsSettersContext.Provider, { value: setters, children: children }) }) }));
61
63
  };
62
64
  exports.SequencePropsSubscriptionProvider = SequencePropsSubscriptionProvider;
@@ -4,7 +4,9 @@ exports.SettingsButton = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const browser_studio_operations_1 = require("../helpers/browser-studio-operations");
7
+ const client_id_1 = require("../helpers/client-id");
7
8
  const colors_1 = require("../helpers/colors");
9
+ const settings_tab_availability_1 = require("../helpers/settings-tab-availability");
8
10
  const gear_1 = require("../icons/gear");
9
11
  const modals_1 = require("../state/modals");
10
12
  const ActionTooltip_1 = require("./ActionTooltip");
@@ -12,11 +14,18 @@ const InlineAction_1 = require("./InlineAction");
12
14
  const UpdateStatusContext_1 = require("./UpdateStatusContext");
13
15
  const SettingsButton = ({ showUpdates }) => {
14
16
  const { setSelectedModal } = (0, react_1.useContext)(modals_1.SetSelectedModalContext);
17
+ const { previewServerState } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
15
18
  const { info, knownBugs } = (0, UpdateStatusContext_1.useUpdateStatus)();
16
19
  const updateAvailable = showUpdates &&
17
20
  Boolean((info === null || info === void 0 ? void 0 : info.updateAvailable) || (info === null || info === void 0 ? void 0 : info.skillsUpdateAvailable));
18
21
  const hasBugfixesAvailable = showUpdates &&
19
22
  Boolean((info === null || info === void 0 ? void 0 : info.updateAvailable) && knownBugs && knownBugs.length > 0);
23
+ const isBrowserStudio = (0, browser_studio_operations_1.getBrowserStudioOperations)() !== null;
24
+ const canEditConfig = (0, settings_tab_availability_1.canEditStudioConfig)({
25
+ isBrowserStudio,
26
+ previewServerConnected: previewServerState.type === 'connected',
27
+ readOnlyStudio: window.remotion_isReadOnlyStudio,
28
+ });
20
29
  const openModal = (0, react_1.useCallback)(() => {
21
30
  var _a;
22
31
  var _b;
@@ -24,12 +33,12 @@ const SettingsButton = ({ showUpdates }) => {
24
33
  type: 'settings',
25
34
  initialTab: updateAvailable
26
35
  ? 'updates'
27
- : (0, browser_studio_operations_1.getBrowserStudioOperations)() === null
36
+ : canEditConfig
28
37
  ? 'studio'
29
38
  : 'shortcuts',
30
39
  initialPublicLicenseKey: (_b = (_a = window.remotion_renderDefaults) === null || _a === void 0 ? void 0 : _a.publicLicenseKey) !== null && _b !== void 0 ? _b : null,
31
40
  });
32
- }, [setSelectedModal, updateAvailable]);
41
+ }, [canEditConfig, setSelectedModal, updateAvailable]);
33
42
  const renderGearIcon = (0, react_1.useCallback)((color) => {
34
43
  return jsx_runtime_1.jsx(gear_1.GearIcon, { color: color, width: 16, height: 16 });
35
44
  }, []);
@@ -1,7 +1,6 @@
1
1
  import React from 'react';
2
- type SettingsTab = 'apps' | 'rendering' | 'studio' | 'packages' | 'models' | 'shortcuts' | 'skills' | 'license' | 'updates';
2
+ import { type SettingsTab } from '../helpers/settings-tab-availability';
3
3
  export declare const SettingsModal: React.FC<{
4
4
  readonly initialTab: SettingsTab;
5
5
  readonly initialPublicLicenseKey: string | null;
6
6
  }>;
7
- export {};
@@ -6,6 +6,7 @@ const react_1 = require("react");
6
6
  const browser_studio_operations_1 = require("../helpers/browser-studio-operations");
7
7
  const can_show_updates_1 = require("../helpers/can-show-updates");
8
8
  const client_id_1 = require("../helpers/client-id");
9
+ const settings_tab_availability_1 = require("../helpers/settings-tab-availability");
9
10
  const apps_1 = require("../icons/apps");
10
11
  const cloud_download_1 = require("../icons/cloud-download");
11
12
  const keyboard_1 = require("../icons/keyboard");
@@ -77,8 +78,20 @@ const SettingsModal = ({ initialPublicLicenseKey, initialTab }) => {
77
78
  const packageManager = window.remotion_packageManager === 'unknown'
78
79
  ? null
79
80
  : window.remotion_packageManager;
80
- const showPackages = (0, browser_studio_operations_1.canInstallPackages)() && (isBrowserStudio || packageManager !== null);
81
- const safeInitialTab = initialTab === 'updates' && !showUpdates ? 'shortcuts' : initialTab;
81
+ const previewServerConnected = previewServerState.type === 'connected';
82
+ const canEditConfig = (0, settings_tab_availability_1.canEditStudioConfig)({
83
+ isBrowserStudio,
84
+ previewServerConnected,
85
+ readOnlyStudio: window.remotion_isReadOnlyStudio,
86
+ });
87
+ const availableTabs = (0, react_1.useMemo)(() => (0, settings_tab_availability_1.getAvailableSettingsTabs)({
88
+ isBrowserStudio,
89
+ packageManagerAvailable: packageManager !== null,
90
+ previewServerConnected,
91
+ readOnlyStudio: window.remotion_isReadOnlyStudio,
92
+ showUpdates,
93
+ }), [isBrowserStudio, packageManager, previewServerConnected, showUpdates]);
94
+ const safeInitialTab = (0, settings_tab_availability_1.getSafeSettingsTab)(initialTab, availableTabs);
82
95
  const [tab, setTab] = (0, react_1.useState)(safeInitialTab);
83
96
  const [openedTabs, setOpenedTabs] = (0, react_1.useState)([safeInitialTab]);
84
97
  const [packagesFooterContainer, setPackagesFooterContainer] = (0, react_1.useState)(null);
@@ -95,16 +108,21 @@ const SettingsModal = ({ initialPublicLicenseKey, initialTab }) => {
95
108
  });
96
109
  }, []);
97
110
  (0, react_1.useLayoutEffect)(() => {
98
- if (!showUpdates && tab === 'updates') {
99
- selectTab('shortcuts');
111
+ const safeTab = (0, settings_tab_availability_1.getSafeSettingsTab)(tab, availableTabs);
112
+ if (safeTab !== tab) {
113
+ selectTab(safeTab);
100
114
  }
101
- }, [selectTab, showUpdates, tab]);
115
+ }, [availableTabs, selectTab, tab]);
102
116
  (0, react_1.useEffect)(() => {
103
117
  setPublicLicenseKey(initialPublicLicenseKey);
104
118
  }, [initialPublicLicenseKey, setPublicLicenseKey]);
105
119
  return (jsx_runtime_1.jsx(DismissableModal_1.DismissableModal, { panelStyle: render_modals_1.outerModalStyle, children: jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
106
120
  jsx_runtime_1.jsx(ModalHeader_1.ModalHeader, { title: "Settings", onClose: dismiss }), jsx_runtime_1.jsxs("div", { style: render_modals_1.horizontalLayout, children: [
107
- jsx_runtime_1.jsxs("div", { style: settingsLeftSidebar, children: [isBrowserStudio ? null : (jsx_runtime_1.jsx(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'studio', onClick: () => selectTab('studio'), renderIcon: (color) => (jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(remotion_triangle_1.RemotionTriangleIcon, { color: color, style: render_modals_1.icon }) })), children: "Studio" })), isBrowserStudio ? null : (jsx_runtime_1.jsx(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'rendering', onClick: () => selectTab('rendering'), renderIcon: (color) => (jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(video_1.FilmIcon, { color: color, style: render_modals_1.icon }) })), children: "Defaults" })), jsx_runtime_1.jsx(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'shortcuts', onClick: () => selectTab('shortcuts'), renderIcon: (color) => (jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(keyboard_1.KeyboardIcon, { color: color, style: keyboardIcon }) })), children: "Shortcuts" }), showPackages ? (jsx_runtime_1.jsx(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'packages', onClick: () => selectTab('packages'), renderIcon: (color) => (jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(package_1.PackageIcon, { color: color, style: render_modals_1.icon }) })), children: "Packages" })) : null, isBrowserStudio ? null : (jsx_runtime_1.jsx(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'skills', onClick: () => selectTab('skills'), renderIcon: (color) => (jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(skills_1.SkillsIcon, { color: color, style: skillsIcon }) })), children: "Skills" })), jsx_runtime_1.jsx(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'models', onClick: () => selectTab('models'), renderIcon: (color) => (jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(models_1.ModelsIcon, { color: color, style: render_modals_1.icon }) })), children: "Models" }), isBrowserStudio ? null : (jsx_runtime_1.jsx(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'apps', onClick: () => selectTab('apps'), renderIcon: (color) => (jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(apps_1.AppsIcon, { color: color, style: appsIcon }) })), children: "Apps" })), isBrowserStudio ? null : (jsx_runtime_1.jsx(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'license', onClick: () => selectTab('license'), renderIcon: (color) => (jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(license_1.LicenseIcon, { color: color, style: render_modals_1.icon }) })), children: "License" })), showUpdates ? (jsx_runtime_1.jsx(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'updates', onClick: () => selectTab('updates'), renderIcon: (color) => (jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(cloud_download_1.CloudDownloadIcon, { color: color, style: render_modals_1.icon }) })), children: "Updates" })) : null] }), openedTabs.includes('packages') ? (jsx_runtime_1.jsx("div", { style: tab === 'packages' ? render_modals_1.optionsPanel : hiddenPanel, children: jsx_runtime_1.jsx(InstallPackage_1.InstallPackageSettings, { footerContainer: packagesFooterContainer, packageManager: packageManager }) })) : null, openedTabs.includes('apps') ? (jsx_runtime_1.jsx("div", { style: tab === 'apps' ? settingsOptionsPanel : hiddenPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(ConfigureDefaultEditorModal_1.DefaultEditorSettings, {}) })) : null, openedTabs.includes('models') ? (jsx_runtime_1.jsx("div", { style: tab === 'models' ? settingsOptionsPanel : hiddenPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(ModelsSettings_1.ModelsSettings, {}) })) : null, openedTabs.includes('license') ? (jsx_runtime_1.jsx("div", { style: tab === 'license' ? settingsOptionsPanel : hiddenPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(ConfigureLicenseModal_1.LicenseSettings, {}) })) : null, openedTabs.includes('skills') ? (jsx_runtime_1.jsx("div", { style: tab === 'skills' ? settingsOptionsPanel : hiddenPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(SkillsSettings_1.SkillsSettings, {}) })) : null, openedTabs.includes('shortcuts') ? (jsx_runtime_1.jsx("div", { style: tab === 'shortcuts' ? settingsOptionsPanel : hiddenPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(KeyboardShortcutsSettings_1.KeyboardShortcutsSettings, {}) })) : null, openedTabs.includes('rendering') ? (jsx_runtime_1.jsx("div", { style: tab === 'rendering' ? settingsOptionsPanel : hiddenPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(RenderingSettings_1.RenderingSettings, {}) })) : null, openedTabs.includes('studio') ? (jsx_runtime_1.jsx("div", { style: tab === 'studio' ? settingsOptionsPanel : hiddenPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(StudioSettings_1.StudioSettings, {}) })) : null, showUpdates && openedTabs.includes('updates') ? (jsx_runtime_1.jsx("div", { style: tab === 'updates' ? settingsOptionsPanel : hiddenPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(UpdatesSettings_1.UpdatesSettings, {}) })) : null] }), tab === 'packages' ? (jsx_runtime_1.jsx("div", { ref: setPackagesFooterContainer })) : isBrowserStudio ||
121
+ jsx_runtime_1.jsxs("div", { style: settingsLeftSidebar, children: [availableTabs.includes('studio') ? (jsx_runtime_1.jsx(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'studio', onClick: () => selectTab('studio'), renderIcon: (color) => (jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(remotion_triangle_1.RemotionTriangleIcon, { color: color, style: render_modals_1.icon }) })), children: "Studio" })) : null, availableTabs.includes('rendering') ? (jsx_runtime_1.jsx(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'rendering', onClick: () => selectTab('rendering'), renderIcon: (color) => (jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(video_1.FilmIcon, { color: color, style: render_modals_1.icon }) })), children: "Defaults" })) : null, jsx_runtime_1.jsx(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'shortcuts', onClick: () => selectTab('shortcuts'), renderIcon: (color) => (jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(keyboard_1.KeyboardIcon, { color: color, style: keyboardIcon }) })), children: "Shortcuts" }), availableTabs.includes('packages') ? (jsx_runtime_1.jsx(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'packages', onClick: () => selectTab('packages'), renderIcon: (color) => (jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(package_1.PackageIcon, { color: color, style: render_modals_1.icon }) })), children: "Packages" })) : null, availableTabs.includes('skills') ? (jsx_runtime_1.jsx(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'skills', onClick: () => selectTab('skills'), renderIcon: (color) => (jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(skills_1.SkillsIcon, { color: color, style: skillsIcon }) })), children: "Skills" })) : null, availableTabs.includes('models') ? (jsx_runtime_1.jsx(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'models', onClick: () => selectTab('models'), renderIcon: (color) => (jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(models_1.ModelsIcon, { color: color, style: render_modals_1.icon }) })), children: "Models" })) : null, availableTabs.includes('apps') ? (jsx_runtime_1.jsx(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'apps', onClick: () => selectTab('apps'), renderIcon: (color) => (jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(apps_1.AppsIcon, { color: color, style: appsIcon }) })), children: "Apps" })) : null, availableTabs.includes('license') ? (jsx_runtime_1.jsx(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'license', onClick: () => selectTab('license'), renderIcon: (color) => (jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(license_1.LicenseIcon, { color: color, style: render_modals_1.icon }) })), children: "License" })) : null, availableTabs.includes('updates') ? (jsx_runtime_1.jsx(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'updates', onClick: () => selectTab('updates'), renderIcon: (color) => (jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(cloud_download_1.CloudDownloadIcon, { color: color, style: render_modals_1.icon }) })), children: "Updates" })) : null] }), availableTabs.includes('packages') &&
122
+ openedTabs.includes('packages') ? (jsx_runtime_1.jsx("div", { style: tab === 'packages' ? render_modals_1.optionsPanel : hiddenPanel, children: jsx_runtime_1.jsx(InstallPackage_1.InstallPackageSettings, { footerContainer: packagesFooterContainer, packageManager: packageManager }) })) : null, availableTabs.includes('apps') && openedTabs.includes('apps') ? (jsx_runtime_1.jsx("div", { style: tab === 'apps' ? settingsOptionsPanel : hiddenPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(ConfigureDefaultEditorModal_1.DefaultEditorSettings, {}) })) : null, availableTabs.includes('models') && openedTabs.includes('models') ? (jsx_runtime_1.jsx("div", { style: tab === 'models' ? settingsOptionsPanel : hiddenPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(ModelsSettings_1.ModelsSettings, {}) })) : null, availableTabs.includes('license') &&
123
+ openedTabs.includes('license') ? (jsx_runtime_1.jsx("div", { style: tab === 'license' ? settingsOptionsPanel : hiddenPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(ConfigureLicenseModal_1.LicenseSettings, {}) })) : null, availableTabs.includes('skills') && openedTabs.includes('skills') ? (jsx_runtime_1.jsx("div", { style: tab === 'skills' ? settingsOptionsPanel : hiddenPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(SkillsSettings_1.SkillsSettings, {}) })) : null, openedTabs.includes('shortcuts') ? (jsx_runtime_1.jsx("div", { style: tab === 'shortcuts' ? settingsOptionsPanel : hiddenPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(KeyboardShortcutsSettings_1.KeyboardShortcutsSettings, {}) })) : null, availableTabs.includes('rendering') &&
124
+ openedTabs.includes('rendering') ? (jsx_runtime_1.jsx("div", { style: tab === 'rendering' ? settingsOptionsPanel : hiddenPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(RenderingSettings_1.RenderingSettings, {}) })) : null, availableTabs.includes('studio') && openedTabs.includes('studio') ? (jsx_runtime_1.jsx("div", { style: tab === 'studio' ? settingsOptionsPanel : hiddenPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(StudioSettings_1.StudioSettings, {}) })) : null, availableTabs.includes('updates') &&
125
+ openedTabs.includes('updates') ? (jsx_runtime_1.jsx("div", { style: tab === 'updates' ? settingsOptionsPanel : hiddenPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: jsx_runtime_1.jsx(UpdatesSettings_1.UpdatesSettings, {}) })) : null] }), tab === 'packages' ? (jsx_runtime_1.jsx("div", { ref: setPackagesFooterContainer })) : !canEditConfig ||
108
126
  tab === 'models' ||
109
127
  tab === 'updates' ||
110
128
  tab === 'skills' ? null : (jsx_runtime_1.jsx(SettingsModalFooter_1.SettingsModalFooter, { showLicenseFaq: tab === 'license' }))] }) }));
@@ -3,17 +3,22 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SettingsModalFooter = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
+ const restart_studio_1 = require("../api/restart-studio");
7
+ const client_id_1 = require("../helpers/client-id");
6
8
  const colors_1 = require("../helpers/colors");
7
9
  const InspectorOpenInEditor_1 = require("./InspectorOpenInEditor");
8
10
  const common_1 = require("./InspectorPanel/common");
9
11
  const layout_1 = require("./layout");
12
+ const ModalButton_1 = require("./ModalButton");
10
13
  const ModalFooter_1 = require("./ModalFooter");
14
+ const NotificationCenter_1 = require("./Notifications/NotificationCenter");
11
15
  const footer = {
12
16
  flex: 'none',
13
17
  };
14
18
  const footerRow = {
15
19
  alignItems: 'center',
16
20
  display: 'flex',
21
+ height: 28,
17
22
  justifyContent: 'space-between',
18
23
  };
19
24
  const configFileHint = {
@@ -40,6 +45,8 @@ const externalLinkIndicator = {
40
45
  width: 12,
41
46
  };
42
47
  const SettingsModalFooter = ({ showLicenseFaq }) => {
48
+ const { restartRequired } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
49
+ const [restarting, setRestarting] = (0, react_1.useState)(false);
43
50
  const configFileLocation = (0, react_1.useMemo)(() => {
44
51
  return {
45
52
  source: 'remotion.config.ts',
@@ -50,10 +57,17 @@ const SettingsModalFooter = ({ showLicenseFaq }) => {
50
57
  const openLicenseFaq = (0, react_1.useCallback)(() => {
51
58
  window.open('https://www.remotion.dev/docs/license/faq', '_blank', 'noopener,noreferrer');
52
59
  }, []);
60
+ const restart = (0, react_1.useCallback)(() => {
61
+ setRestarting(true);
62
+ (0, restart_studio_1.restartStudio)().catch((error) => {
63
+ setRestarting(false);
64
+ (0, NotificationCenter_1.showNotification)(`Could not restart Studio: ${error.message}`, 4000);
65
+ });
66
+ }, []);
53
67
  return (jsx_runtime_1.jsx(ModalFooter_1.ModalFooterContainer, { style: footer, children: jsx_runtime_1.jsxs("div", { style: footerRow, children: [
54
- jsx_runtime_1.jsxs("div", { style: configFileHint, children: ["Changes save to",
55
- jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 }), jsx_runtime_1.jsx(InspectorOpenInEditor_1.InspectorOpenInEditor, { locationType: null, location: configFileLocation, label: jsx_runtime_1.jsx("strong", { style: configFileName, children: "remotion.config.ts" }), showTooltips: false })
56
- ] }), showLicenseFaq ? (jsx_runtime_1.jsxs(common_1.InspectorQuickAction, { disabled: false, onClick: openLicenseFaq, style: { flex: 'none', width: 'fit-content' }, title: "Open the Remotion License FAQ in a new tab", children: ["License FAQ",
68
+ jsx_runtime_1.jsx("div", { style: configFileHint, children: restartRequired ? ('Restart the server to apply changes') : (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: ["Changes save to",
69
+ jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 }), jsx_runtime_1.jsx(InspectorOpenInEditor_1.InspectorOpenInEditor, { locationType: null, location: configFileLocation, label: jsx_runtime_1.jsx("strong", { style: configFileName, children: "remotion.config.ts" }), showTooltips: false })
70
+ ] })) }), restartRequired ? (jsx_runtime_1.jsx(ModalButton_1.ModalButton, { disabled: restarting, onClick: restart, size: "compact", title: "Restart Studio to apply config file changes", children: restarting ? 'Restarting...' : 'Restart Studio' })) : showLicenseFaq ? (jsx_runtime_1.jsxs(common_1.InspectorQuickAction, { disabled: false, onClick: openLicenseFaq, style: { flex: 'none', width: 'fit-content' }, title: "Open the Remotion License FAQ in a new tab", children: ["License FAQ",
57
71
  jsx_runtime_1.jsx("svg", { "aria-hidden": "true", viewBox: "0 0 16 16", style: externalLinkIndicator, children: jsx_runtime_1.jsx("path", { d: "M4 12 12 4M6 4h6v6", fill: "none", stroke: colors_1.CURRENT_COLOR, strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "1.5" }) })
58
72
  ] })) : null] }) }));
59
73
  };
@@ -1,2 +1,7 @@
1
+ import type { GetRemotionSkillsInfoResponse } from '@remotion/studio-shared';
1
2
  import React from 'react';
3
+ export declare const SkillSettingsRow: React.FC<{
4
+ readonly isLast: boolean;
5
+ readonly skill: GetRemotionSkillsInfoResponse['skills'][number];
6
+ }>;
2
7
  export declare const SkillsSettings: React.FC;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SkillsSettings = void 0;
3
+ exports.SkillsSettings = exports.SkillSettingsRow = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const client_id_1 = require("../helpers/client-id");
@@ -107,9 +107,35 @@ const loading = {
107
107
  ...description,
108
108
  marginTop: 14,
109
109
  };
110
+ const SkillSettingsRow = ({ isLast, skill }) => {
111
+ const { installSkill, removeSkill, skillAction } = (0, SettingsContext_1.useSettings)();
112
+ const { previewServerState } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
113
+ const canInstall = !window.remotion_isReadOnlyStudio &&
114
+ previewServerState.type === 'connected';
115
+ const installed = skill.installedInProject || skill.installedGlobally;
116
+ const processingThisSkill = (skillAction === null || skillAction === void 0 ? void 0 : skillAction.skill) === skill.name;
117
+ const installedLocation = skill.installedInProject && skill.installedGlobally
118
+ ? 'Project and global'
119
+ : skill.installedInProject
120
+ ? 'Project'
121
+ : skill.installedGlobally
122
+ ? 'Global'
123
+ : null;
124
+ const renderInstallAction = (0, react_1.useCallback)((color) => {
125
+ return jsx_runtime_1.jsx(cloud_download_1.CloudDownloadIcon, { color: color, style: actionIcon });
126
+ }, []);
127
+ const renderRemoveAction = (0, react_1.useCallback)((color) => {
128
+ return jsx_runtime_1.jsx(trash_1.TrashIcon, { color: color, style: actionIcon });
129
+ }, []);
130
+ return (jsx_runtime_1.jsxs("div", { role: "listitem", style: isLast ? lastSkillRow : skillRow, children: [
131
+ jsx_runtime_1.jsxs("span", { style: skillName, children: ["/", skill.name] }), processingThisSkill ? (jsx_runtime_1.jsx("span", { style: status, children: skillAction.type === 'installing' ? 'Installing…' : 'Removing…' })) : installedLocation ? (jsx_runtime_1.jsx("span", { style: status, children: installedLocation })) : null, installed ? (jsx_runtime_1.jsx(check_circle_filled_1.CheckCircleFilled, { "aria-hidden": true, style: { ...statusIcon, fill: colors_1.BLUE } })) : null, processingThisSkill ? (jsx_runtime_1.jsx("span", { style: actionSlot, children: jsx_runtime_1.jsx(Spinner_1.Spinner, { duration: 0.5, size: 14 }) })) : installed && canInstall ? (jsx_runtime_1.jsx(InlineAction_1.InlineAction, { title: skill.installedInProject
132
+ ? `Remove ${skill.name} from this project`
133
+ : `Remove ${skill.name} globally`, disabled: skillAction !== null, onClick: () => removeSkill(skill.name), renderAction: renderRemoveAction, variant: null })) : canInstall ? (jsx_runtime_1.jsx(InlineAction_1.InlineAction, { title: `Install ${skill.name} in this project`, disabled: skillAction !== null, onClick: () => installSkill(skill.name), renderAction: renderInstallAction, variant: null })) : null] }));
134
+ };
135
+ exports.SkillSettingsRow = SkillSettingsRow;
110
136
  const SkillsSettings = () => {
111
137
  var _a;
112
- const { error, remotionSkillsInfo, installSkill, removeSkill, skillAction, skillActionError, } = (0, SettingsContext_1.useSettings)();
138
+ const { error, remotionSkillsInfo, skillActionError } = (0, SettingsContext_1.useSettings)();
113
139
  const { previewServerState } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
114
140
  const canInstall = !window.remotion_isReadOnlyStudio &&
115
141
  previewServerState.type === 'connected';
@@ -129,35 +155,12 @@ const SkillsSettings = () => {
129
155
  const renderCopyAction = (0, react_1.useCallback)((color) => {
130
156
  return jsx_runtime_1.jsx(copy_1.CopyIcon, { copied: copied, color: color, style: copyIcon });
131
157
  }, [copied]);
132
- const renderInstallAction = (0, react_1.useCallback)((color) => {
133
- return jsx_runtime_1.jsx(cloud_download_1.CloudDownloadIcon, { color: color, style: actionIcon });
134
- }, []);
135
- const renderRemoveAction = (0, react_1.useCallback)((color) => {
136
- return jsx_runtime_1.jsx(trash_1.TrashIcon, { color: color, style: actionIcon });
137
- }, []);
138
- const actionInProgress = skillAction !== null;
139
158
  return (jsx_runtime_1.jsxs("div", { style: container, children: [remotionSkillsInfo === null && error === null ? (jsx_runtime_1.jsx("p", { style: loading, children: "Checking installed skills..." })) : null, remotionSkillsInfo === null && error ? (jsx_runtime_1.jsx("div", { style: { marginTop: 14 }, children: jsx_runtime_1.jsx(ValidationMessage_1.ValidationMessage, { message: error, align: "flex-start", type: "error" }) })) : null, missingSkills > 0 && canInstall ? (jsx_runtime_1.jsx("p", { style: description, children: "Install skills in this project to use them with your coding agent." })) : null, skillActionError ? (jsx_runtime_1.jsx("div", { style: { marginTop: 14 }, children: jsx_runtime_1.jsx(ValidationMessage_1.ValidationMessage, { message: skillActionError, align: "flex-start", type: "error" }) })) : null, missingSkills > 0 && !canInstall ? (jsx_runtime_1.jsxs("div", { children: [
140
159
  jsx_runtime_1.jsx("p", { style: description, children: "Not all skills are installed. Run this command in the project directory, then reload Studio and restart your coding agent." }), jsx_runtime_1.jsxs("div", { style: commandField, children: [
141
160
  jsx_runtime_1.jsx("pre", { style: command, children: INSTALL_COMMAND }), jsx_runtime_1.jsx(InlineAction_1.InlineAction, { variant: null, onClick: onCopy, renderAction: renderCopyAction, title: "Copy install command" })
142
161
  ] })
143
162
  ] })) : null, remotionSkillsInfo ? (jsx_runtime_1.jsx("div", { style: list, role: "list", "aria-label": "Remotion Agent Skills", children: remotionSkillsInfo.skills.map((skill, index) => {
144
- const installed = skill.installedInProject || skill.installedGlobally;
145
- const processingThisSkill = (skillAction === null || skillAction === void 0 ? void 0 : skillAction.skill) === skill.name;
146
- const installedLocation = skill.installedInProject && skill.installedGlobally
147
- ? 'Project and global'
148
- : skill.installedInProject
149
- ? 'Project'
150
- : skill.installedGlobally
151
- ? 'Global'
152
- : null;
153
- return (jsx_runtime_1.jsxs("div", { role: "listitem", style: index === remotionSkillsInfo.skills.length - 1
154
- ? lastSkillRow
155
- : skillRow, children: [
156
- jsx_runtime_1.jsxs("span", { style: skillName, children: ["/", skill.name] }), processingThisSkill ? (jsx_runtime_1.jsx("span", { style: status, children: skillAction.type === 'installing'
157
- ? 'Installing…'
158
- : 'Removing…' })) : installedLocation ? (jsx_runtime_1.jsx("span", { style: status, children: installedLocation })) : null, installed ? (jsx_runtime_1.jsx(check_circle_filled_1.CheckCircleFilled, { "aria-hidden": true, style: { ...statusIcon, fill: colors_1.BLUE } })) : null, processingThisSkill ? (jsx_runtime_1.jsx("span", { style: actionSlot, children: jsx_runtime_1.jsx(Spinner_1.Spinner, { duration: 0.5, size: 14 }) })) : installed && canInstall ? (jsx_runtime_1.jsx(InlineAction_1.InlineAction, { title: skill.installedInProject
159
- ? `Remove ${skill.name} from this project`
160
- : `Remove ${skill.name} globally`, disabled: actionInProgress, onClick: () => removeSkill(skill.name), renderAction: renderRemoveAction, variant: null })) : canInstall ? (jsx_runtime_1.jsx(InlineAction_1.InlineAction, { title: `Install ${skill.name} in this project`, disabled: actionInProgress, onClick: () => installSkill(skill.name), renderAction: renderInstallAction, variant: null })) : null] }, skill.name));
163
+ return (jsx_runtime_1.jsx(exports.SkillSettingsRow, { isLast: index === remotionSkillsInfo.skills.length - 1, skill: skill }, skill.name));
161
164
  }) })) : null] }));
162
165
  };
163
166
  exports.SkillsSettings = SkillsSettings;
@@ -47,7 +47,6 @@ const interactivity_enabled_1 = require("../../helpers/interactivity-enabled");
47
47
  const is_current_selected_still_1 = require("../../helpers/is-current-selected-still");
48
48
  const open_in_editor_1 = require("../../helpers/open-in-editor");
49
49
  const studio_runtime_config_1 = require("../../helpers/studio-runtime-config");
50
- const timeline_node_path_key_1 = require("../../helpers/timeline-node-path-key");
51
50
  const use_sync_external_store_1 = require("../../helpers/use-sync-external-store");
52
51
  const call_api_1 = require("../call-api");
53
52
  const ContextMenu_1 = require("../ContextMenu");
@@ -62,6 +61,7 @@ const should_show_track_in_timeline_1 = require("./should-show-track-in-timeline
62
61
  const should_subscribe_to_sequence_props_1 = require("./should-subscribe-to-sequence-props");
63
62
  const SubscribeToNodePaths_1 = require("./SubscribeToNodePaths");
64
63
  const timeline_asset_drop_context_1 = require("./timeline-asset-drop-context");
64
+ const timeline_display_groups_1 = require("./timeline-display-groups");
65
65
  const timeline_refs_1 = require("./timeline-refs");
66
66
  const TimelineDragHandler_1 = require("./TimelineDragHandler");
67
67
  const TimelineHeightContainer_1 = require("./TimelineHeightContainer");
@@ -248,11 +248,11 @@ const TimelineInner = () => {
248
248
  if (videoConfigIsNull) {
249
249
  return [];
250
250
  }
251
- return (0, calculate_timeline_1.calculateTimeline)({
251
+ return (0, timeline_display_groups_1.addTimelineDisplayGroups)((0, calculate_timeline_1.calculateTimeline)({
252
252
  sequences,
253
253
  overrideIdsToNodePaths: overrideIdToNodePathMappings,
254
254
  compositions,
255
- });
255
+ }));
256
256
  }, [
257
257
  sequences,
258
258
  videoConfigIsNull,
@@ -277,16 +277,15 @@ const TimelineInner = () => {
277
277
  // Keep `filtered` complete so a future toggle can show every programmatic
278
278
  // instance without recalculating the timeline or losing its instance index.
279
279
  const collapsed = (0, react_1.useMemo)(() => {
280
- const seenNodePaths = new Set();
280
+ const seenDisplayGroups = new Set();
281
281
  return filtered.filter((track) => {
282
- if (track.nodePathInfo === null) {
282
+ if (track.displayGroup === null) {
283
283
  return true;
284
284
  }
285
- const key = (0, timeline_node_path_key_1.timelineSequenceNodePathToKey)(track.nodePathInfo.sequenceSubscriptionKey);
286
- if (seenNodePaths.has(key)) {
285
+ if (seenDisplayGroups.has(track.displayGroup.key)) {
287
286
  return false;
288
287
  }
289
- seenNodePaths.add(key);
288
+ seenDisplayGroups.add(track.displayGroup.key);
290
289
  return true;
291
290
  });
292
291
  }, [filtered]);
@@ -294,7 +293,7 @@ const TimelineInner = () => {
294
293
  const pendingInsertedElementSelection = (0, use_sync_external_store_1.useSyncExternalStore)(inserted_element_selection_1.subscribeToInsertedElementSelection, inserted_element_selection_1.getInsertedElementSelection, inserted_element_selection_1.getInsertedElementSelection);
295
294
  const { fastRefreshes } = (0, react_1.useContext)(fast_refresh_context_1.FastRefreshContext);
296
295
  const pendingSelectionStart = (0, react_1.useRef)(null);
297
- const { selectItems } = (0, TimelineSelection_1.useTimelineSelection)();
296
+ const currentSelection = (0, TimelineSelection_1.useCurrentTimelineSelectionStateAsRef)();
298
297
  (0, react_1.useLayoutEffect)(() => {
299
298
  var _a;
300
299
  if (pendingInsertedElementSelection === null) {
@@ -342,17 +341,17 @@ const TimelineInner = () => {
342
341
  if (insertedTrack.nodePathInfo === null) {
343
342
  return;
344
343
  }
345
- selectItems([{ type: 'sequence', nodePathInfo: insertedTrack.nodePathInfo }], { reveal: true });
344
+ currentSelection.current.selectItems([{ type: 'sequence', nodePathInfo: insertedTrack.nodePathInfo }], { reveal: true });
346
345
  (0, inserted_element_selection_1.clearInsertedElementSelection)(pendingInsertedElementSelection);
347
346
  if (pendingInsertedElementSelection.notification !== null) {
348
347
  (0, NotificationCenter_1.showNotification)(pendingInsertedElementSelection.notification, 3000);
349
348
  }
350
349
  }, [
351
350
  canvasContent,
351
+ currentSelection,
352
352
  fastRefreshes,
353
353
  layerChildrenValue,
354
354
  pendingInsertedElementSelection,
355
- selectItems,
356
355
  timeline,
357
356
  ]);
358
357
  const maxTimelineTracks = (0, studio_runtime_config_1.getStudioMaxTimelineTracks)();
@@ -10,6 +10,8 @@ const colors_1 = require("../../helpers/colors");
10
10
  const hoverable_1 = require("../../helpers/hoverable");
11
11
  const pen_1 = require("../../icons/pen");
12
12
  const modals_1 = require("../../state/modals");
13
+ const asset_context_menu_1 = require("../asset-context-menu");
14
+ const ContextMenu_1 = require("../ContextMenu");
13
15
  const import_assets_1 = require("../import-assets");
14
16
  const InlineAction_1 = require("../InlineAction");
15
17
  const common_1 = require("../InspectorPanel/common");
@@ -44,24 +46,35 @@ const imageAssetField = {
44
46
  alignItems: 'center',
45
47
  display: 'flex',
46
48
  flex: 1,
47
- gap: 8,
49
+ gap: 4,
50
+ margin: '0 4px',
51
+ minWidth: 0,
52
+ };
53
+ const imageAssetPreviewContainer = {
54
+ display: 'flex',
55
+ flex: 1,
48
56
  minWidth: 0,
49
- padding: '4px 12px',
50
57
  };
51
- const thumbnailButton = {
52
- ...(0, hoverable_1.hoverableStyle)({
53
- idleBackground: colors_1.TRANSPARENT,
54
- hoverBackground: colors_1.TRANSPARENT,
55
- idleColor: colors_1.LIGHT_TEXT,
56
- hoverColor: colors_1.WHITE,
57
- }),
58
+ const imageAssetPreviewButton = {
59
+ alignItems: 'center',
58
60
  appearance: 'none',
59
61
  border: 'none',
62
+ borderRadius: 4,
60
63
  cursor: 'default',
61
- flexShrink: 0,
64
+ display: 'flex',
65
+ flex: 1,
66
+ gap: 8,
62
67
  height: 40,
63
68
  margin: 0,
64
- padding: 0,
69
+ minWidth: 0,
70
+ padding: '0 8px',
71
+ textAlign: 'left',
72
+ };
73
+ const thumbnailContainer = {
74
+ borderRadius: 4,
75
+ flexShrink: 0,
76
+ height: 40,
77
+ overflow: 'hidden',
65
78
  width: 40,
66
79
  };
67
80
  const thumbnail = {
@@ -71,22 +84,11 @@ const thumbnail = {
71
84
  width: '100%',
72
85
  };
73
86
  const imageAssetInfo = {
74
- ...(0, hoverable_1.hoverableStyle)({
75
- idleBackground: colors_1.TRANSPARENT,
76
- hoverBackground: colors_1.TRANSPARENT,
77
- idleColor: colors_1.LIGHT_TEXT,
78
- hoverColor: colors_1.WHITE,
79
- }),
80
- appearance: 'none',
81
- border: 'none',
82
87
  display: 'flex',
83
88
  flex: 1,
84
89
  flexDirection: 'column',
85
90
  gap: 2,
86
- margin: 0,
87
91
  minWidth: 0,
88
- padding: 0,
89
- textAlign: 'left',
90
92
  };
91
93
  const imageAssetName = {
92
94
  color: colors_1.WHITE,
@@ -127,6 +129,14 @@ const TimelineAssetField = ({ field, effectiveValue, propStatus, onSave, onDragV
127
129
  const { getSourceAction, sourceAction } = (0, react_1.useContext)(exports.AssetSelectionContext);
128
130
  const { assetType } = field.fieldSchema;
129
131
  const initialQuery = assetType ? `type:${assetType} ` : '';
132
+ const imageLinkInfo = assetType === 'image' && typeof effectiveValue === 'string'
133
+ ? (0, timeline_asset_link_1.getTimelineAssetLinkInfo)(effectiveValue)
134
+ : null;
135
+ const localAssetPath = (imageLinkInfo === null || imageLinkInfo === void 0 ? void 0 : imageLinkInfo.kind) === 'local' ? imageLinkInfo.assetPath : null;
136
+ const { getContextMenuItems: getLocalAssetContextMenuItems } = (0, asset_context_menu_1.useAssetContextMenuItems)({
137
+ relativePath: localAssetPath,
138
+ readOnlyStudio: window.remotion_isReadOnlyStudio,
139
+ });
130
140
  const inlineSourceAction = (0, react_1.useMemo)(() => {
131
141
  if (typeof effectiveValue === 'string') {
132
142
  return getSourceAction(effectiveValue);
@@ -220,38 +230,58 @@ const TimelineAssetField = ({ field, effectiveValue, propStatus, onSave, onDragV
220
230
  setSelectedModal,
221
231
  ]);
222
232
  const action = (jsx_runtime_1.jsx(InlineAction_1.InlineAction, { variant: null, onClick: openAssetSelection, disabled: window.remotion_isReadOnlyStudio, title: "Change source", renderAction: (color) => jsx_runtime_1.jsx(pen_1.PenIcon, { color: color, style: penIcon }) }));
223
- if (assetType === 'image' && typeof effectiveValue === 'string') {
224
- const linkInfo = (0, timeline_asset_link_1.getTimelineAssetLinkInfo)(effectiveValue);
225
- if (linkInfo !== null) {
226
- let name;
227
- let source;
228
- let previewSrc;
229
- if (linkInfo.kind === 'local') {
230
- name = (_a = linkInfo.assetPath.split('/').pop()) !== null && _a !== void 0 ? _a : linkInfo.assetPath;
231
- source = 'Project asset';
232
- previewSrc = (0, remotion_1.staticFile)(linkInfo.assetPath);
233
+ if (imageLinkInfo !== null) {
234
+ const linkInfo = imageLinkInfo;
235
+ let name;
236
+ let source;
237
+ let previewSrc;
238
+ if (linkInfo.kind === 'local') {
239
+ name = (_a = linkInfo.assetPath.split('/').pop()) !== null && _a !== void 0 ? _a : linkInfo.assetPath;
240
+ source = null;
241
+ previewSrc = (0, remotion_1.staticFile)(linkInfo.assetPath);
242
+ }
243
+ else {
244
+ previewSrc = linkInfo.href.startsWith('//')
245
+ ? `https:${linkInfo.href}`
246
+ : linkInfo.href;
247
+ try {
248
+ const url = new URL(previewSrc);
249
+ const encodedName = url.pathname.split('/').filter(Boolean).pop();
250
+ name = encodedName ? decodeURIComponent(encodedName) : url.hostname;
251
+ source = url.hostname;
233
252
  }
234
- else {
235
- previewSrc = linkInfo.href.startsWith('//')
236
- ? `https:${linkInfo.href}`
237
- : linkInfo.href;
238
- try {
239
- const url = new URL(previewSrc);
240
- const encodedName = url.pathname.split('/').filter(Boolean).pop();
241
- name = encodedName ? decodeURIComponent(encodedName) : url.hostname;
242
- source = url.hostname;
243
- }
244
- catch (_c) {
245
- name = 'Remote image';
246
- source = linkInfo.href;
247
- }
253
+ catch (_c) {
254
+ name = 'Remote image';
255
+ source = linkInfo.href;
248
256
  }
249
- const title = (_b = inlineSourceAction === null || inlineSourceAction === void 0 ? void 0 : inlineSourceAction.title) !== null && _b !== void 0 ? _b : linkInfo.title;
250
- return (jsx_runtime_1.jsxs("div", { style: imageAssetField, children: [
251
- jsx_runtime_1.jsx("button", { "aria-label": `Replace ${name}`, className: `${hoverable_1.HOVERABLE_CLASS_NAME} ${hoverable_1.FOCUS_VISIBLE_ONLY_CLASS_NAME}`, disabled: window.remotion_isReadOnlyStudio, onClick: openAssetSelection, style: thumbnailButton, title: title, type: "button", children: jsx_runtime_1.jsx("img", { alt: "", src: previewSrc, style: thumbnail }) }), jsx_runtime_1.jsxs("button", { "aria-label": `Replace ${name}`, className: `${hoverable_1.HOVERABLE_CLASS_NAME} ${hoverable_1.FOCUS_VISIBLE_ONLY_CLASS_NAME}`, disabled: window.remotion_isReadOnlyStudio, onClick: openAssetSelection, style: imageAssetInfo, title: title, type: "button", children: [
252
- jsx_runtime_1.jsx("span", { style: imageAssetName, children: name }), jsx_runtime_1.jsx("span", { style: imageAssetSource, children: source })
253
- ] }), action] }));
254
257
  }
258
+ const opensSource = linkInfo.kind === 'local' &&
259
+ inlineSourceAction !== null &&
260
+ inlineSourceAction.onClick !== null;
261
+ const title = (_b = inlineSourceAction === null || inlineSourceAction === void 0 ? void 0 : inlineSourceAction.title) !== null && _b !== void 0 ? _b : linkInfo.title;
262
+ const onPreviewClick = opensSource
263
+ ? inlineSourceAction.onClick
264
+ : openAssetSelection;
265
+ const previewLabel = `${opensSource ? 'Open' : 'Replace'} ${name}`;
266
+ const previewDisabled = opensSource
267
+ ? inlineSourceAction.disabled
268
+ : window.remotion_isReadOnlyStudio;
269
+ const previewButtonStyle = {
270
+ ...imageAssetPreviewButton,
271
+ ...(0, hoverable_1.hoverableStyle)({
272
+ idleBackground: colors_1.TRANSPARENT,
273
+ hoverBackground: previewDisabled
274
+ ? colors_1.TRANSPARENT
275
+ : (0, colors_1.getBackgroundFromHoverState)({ hovered: true, selected: false }),
276
+ idleColor: colors_1.LIGHT_TEXT,
277
+ hoverColor: previewDisabled ? colors_1.LIGHT_TEXT : colors_1.WHITE,
278
+ }),
279
+ };
280
+ const previewAction = (jsx_runtime_1.jsxs("button", { "aria-label": previewLabel, className: `${hoverable_1.HOVERABLE_CLASS_NAME} ${hoverable_1.FOCUS_VISIBLE_ONLY_CLASS_NAME}`, disabled: previewDisabled, onClick: onPreviewClick, style: previewButtonStyle, title: title, type: "button", children: [
281
+ jsx_runtime_1.jsx("span", { style: thumbnailContainer, children: jsx_runtime_1.jsx("img", { alt: "", draggable: false, src: previewSrc, style: thumbnail }) }), jsx_runtime_1.jsxs("span", { style: imageAssetInfo, children: [
282
+ jsx_runtime_1.jsx("span", { style: imageAssetName, children: name }), source === null ? null : (jsx_runtime_1.jsx("span", { style: imageAssetSource, children: source }))] })
283
+ ] }));
284
+ return (jsx_runtime_1.jsxs("div", { style: imageAssetField, children: [localAssetPath === null ? (jsx_runtime_1.jsx("div", { style: imageAssetPreviewContainer, children: previewAction })) : (jsx_runtime_1.jsx(ContextMenu_1.ContextMenu, { getItems: getLocalAssetContextMenuItems, style: imageAssetPreviewContainer, children: previewAction })), action] }));
255
285
  }
256
286
  if (inlineSourceAction === null) {
257
287
  return action;