@remotion/studio 4.0.515 → 4.0.516

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 (196) hide show
  1. package/dist/api/restart-studio.js +4 -0
  2. package/dist/components/AssetSelector.js +3 -9
  3. package/dist/components/AssetSelectorItem.d.ts +2 -1
  4. package/dist/components/AssetSelectorItem.js +24 -29
  5. package/dist/components/Canvas.js +7 -4
  6. package/dist/components/CaptionInspector.d.ts +2 -0
  7. package/dist/components/CaptionInspector.js +3 -5
  8. package/dist/components/CompositionSelector.js +6 -3
  9. package/dist/components/CompositionSelectorItem.js +9 -10
  10. package/dist/components/ConfigSelect.d.ts +17 -0
  11. package/dist/components/ConfigSelect.js +50 -0
  12. package/dist/components/CurrentAsset.d.ts +10 -2
  13. package/dist/components/CurrentAsset.js +139 -46
  14. package/dist/components/FixComputedValueModal.js +54 -51
  15. package/dist/components/GlobalKeybindings.js +5 -5
  16. package/dist/components/InlineAction.d.ts +1 -1
  17. package/dist/components/InlineAction.js +12 -2
  18. package/dist/components/InlineCaptionInspector.d.ts +2 -0
  19. package/dist/components/InlineCaptionInspector.js +2 -2
  20. package/dist/components/InlineCompositionName.js +1 -1
  21. package/dist/components/InlineEditableTitle.js +3 -2
  22. package/dist/components/InspectorInfoHeader.js +1 -1
  23. package/dist/components/InspectorOpenInEditor.js +48 -72
  24. package/dist/components/InspectorPanel/AlignmentControls.js +2 -3
  25. package/dist/components/InspectorPanel/CollapsibleInspectorSectionHeader.d.ts +7 -0
  26. package/dist/components/InspectorPanel/CollapsibleInspectorSectionHeader.js +39 -0
  27. package/dist/components/InspectorPanel/CompositionInspector.js +14 -7
  28. package/dist/components/InspectorPanel/KeyframeEasingNavigator.js +10 -4
  29. package/dist/components/InspectorPanel/SequenceInspectorHeader.js +2 -1
  30. package/dist/components/InspectorPanel/SequenceSelectionInspector.js +1 -4
  31. package/dist/components/InspectorPanel/common.d.ts +0 -12
  32. package/dist/components/InspectorPanel/common.js +3 -52
  33. package/dist/components/InspectorPanel/use-composition-actions.js +0 -1
  34. package/dist/components/InspectorPanelLayout.d.ts +1 -1
  35. package/dist/components/InspectorPanelLayout.js +1 -1
  36. package/dist/components/InspectorSequenceSection.js +40 -54
  37. package/dist/components/InstallPackage.d.ts +2 -1
  38. package/dist/components/InstallPackage.js +60 -30
  39. package/dist/components/InstallablePackage.js +37 -11
  40. package/dist/components/KeyboardShortcutsSettings.d.ts +2 -0
  41. package/dist/components/KeyboardShortcutsSettings.js +177 -0
  42. package/dist/components/MenuBuildIndicator.js +1 -1
  43. package/dist/components/Modals.js +6 -5
  44. package/dist/components/NewComposition/CodemodFooter.d.ts +0 -1
  45. package/dist/components/NewComposition/CodemodFooter.js +2 -8
  46. package/dist/components/NewComposition/DeleteComposition.js +1 -1
  47. package/dist/components/NewComposition/DeleteFolder.js +1 -1
  48. package/dist/components/NewComposition/DuplicateComposition.js +1 -1
  49. package/dist/components/NewComposition/InputDragger.js +27 -4
  50. package/dist/components/NewComposition/NewComposition.js +1 -1
  51. package/dist/components/NewComposition/NewFolder.js +1 -3
  52. package/dist/components/NewComposition/RenameComposition.js +1 -1
  53. package/dist/components/NewComposition/RenameFolder.js +1 -1
  54. package/dist/components/NewComposition/use-rename-static-file.js +1 -1
  55. package/dist/components/Notifications/NotificationCenter.d.ts +1 -0
  56. package/dist/components/Notifications/NotificationCenter.js +5 -0
  57. package/dist/components/OptionsPanel.js +9 -12
  58. package/dist/components/PreviewToolbar.js +5 -2
  59. package/dist/components/PreviewToolbarOverflowButton.js +30 -0
  60. package/dist/components/QuickSwitcher/ExplorerQuickSwitcherTrigger.js +2 -2
  61. package/dist/components/QuickSwitcher/QuickSwitcherContent.js +14 -30
  62. package/dist/components/RenderButton.js +66 -183
  63. package/dist/components/RenderModal/DataEditor.js +15 -12
  64. package/dist/components/RenderModal/RenderStatusModal.js +0 -1
  65. package/dist/components/RenderModal/SchemaEditor/ZodSwitch.js +3 -1
  66. package/dist/components/RenderModal/SchemaEditor/infer-zod-schema-from-value.d.ts +15 -0
  67. package/dist/components/RenderModal/SchemaEditor/infer-zod-schema-from-value.js +95 -0
  68. package/dist/components/RenderQueue/RenderQueueRemoveItem.js +1 -6
  69. package/dist/components/RenderingSettings.js +7 -4
  70. package/dist/components/RulersAndGuidesToggle.d.ts +2 -0
  71. package/dist/components/RulersAndGuidesToggle.js +23 -0
  72. package/dist/components/SegmentedButton.d.ts +29 -0
  73. package/dist/components/SegmentedButton.js +226 -0
  74. package/dist/components/SelectedOutlineEditingHandles.d.ts +16 -0
  75. package/dist/components/SelectedOutlineEditingHandles.js +66 -0
  76. package/dist/components/SelectedOutlineElement.d.ts +2 -4
  77. package/dist/components/SelectedOutlineElement.js +15 -54
  78. package/dist/components/SelectedOutlineOverlay.js +32 -6
  79. package/dist/components/SelectedOutlinePolygon.d.ts +3 -4
  80. package/dist/components/SelectedOutlinePolygon.js +18 -16
  81. package/dist/components/SelectedOutlineRenderer.js +66 -14
  82. package/dist/components/SettingsModal.d.ts +1 -1
  83. package/dist/components/SettingsModal.js +30 -7
  84. package/dist/components/SettingsModalFooter.js +2 -1
  85. package/dist/components/SkillsSettings.d.ts +2 -0
  86. package/dist/components/SkillsSettings.js +129 -0
  87. package/dist/components/StaticFilePreview.js +7 -1
  88. package/dist/components/StudioSettings.js +7 -48
  89. package/dist/components/Tabs/index.js +1 -1
  90. package/dist/components/Timeline/Timeline.js +0 -1
  91. package/dist/components/Timeline/TimelineAssetField.d.ts +1 -1
  92. package/dist/components/Timeline/TimelineAssetField.js +15 -9
  93. package/dist/components/Timeline/TimelineBooleanField.js +1 -4
  94. package/dist/components/Timeline/TimelineClipboardKeybindings.js +7 -10
  95. package/dist/components/Timeline/TimelineEffectItem.d.ts +1 -0
  96. package/dist/components/Timeline/TimelineEffectItem.js +23 -10
  97. package/dist/components/Timeline/TimelineEffectPropItem.js +15 -2
  98. package/dist/components/Timeline/TimelineExpandedRow.js +5 -4
  99. package/dist/components/Timeline/TimelineFieldLabel.js +4 -2
  100. package/dist/components/Timeline/TimelineFieldRowContent.d.ts +1 -2
  101. package/dist/components/Timeline/TimelineFieldRowContent.js +8 -21
  102. package/dist/components/Timeline/TimelineImageInfo.d.ts +0 -1
  103. package/dist/components/Timeline/TimelineImageInfo.js +13 -32
  104. package/dist/components/Timeline/TimelineKeyframeControls.js +10 -4
  105. package/dist/components/Timeline/TimelinePrimitiveFieldValue.js +1 -1
  106. package/dist/components/Timeline/TimelineRotationField.js +0 -2
  107. package/dist/components/Timeline/TimelineRowChrome.d.ts +0 -1
  108. package/dist/components/Timeline/TimelineRowChrome.js +3 -4
  109. package/dist/components/Timeline/TimelineRowLayoutContext.d.ts +0 -1
  110. package/dist/components/Timeline/TimelineRowLayoutContext.js +1 -4
  111. package/dist/components/Timeline/TimelineScaleField.js +2 -6
  112. package/dist/components/Timeline/TimelineSchemaField.js +9 -3
  113. package/dist/components/Timeline/TimelineSelection.js +1 -1
  114. package/dist/components/Timeline/TimelineSequence.d.ts +2 -0
  115. package/dist/components/Timeline/TimelineSequence.js +110 -19
  116. package/dist/components/Timeline/TimelineSequenceItem.js +5 -5
  117. package/dist/components/Timeline/TimelineSequencePropItem.js +15 -2
  118. package/dist/components/Timeline/TimelineSequenceRightEdgeDragHandle.d.ts +4 -2
  119. package/dist/components/Timeline/TimelineSequenceRightEdgeDragHandle.js +85 -91
  120. package/dist/components/Timeline/TimelineTrack.js +1 -1
  121. package/dist/components/Timeline/TimelineTransformOriginField.js +0 -4
  122. package/dist/components/Timeline/TimelineTranslateField.js +0 -4
  123. package/dist/components/Timeline/TimelineVirtualization.js +3 -1
  124. package/dist/components/Timeline/duplicate-selected-timeline-item.js +3 -12
  125. package/dist/components/Timeline/get-copy-context-for-agents-menu-item.d.ts +4 -0
  126. package/dist/components/Timeline/get-copy-context-for-agents-menu-item.js +26 -0
  127. package/dist/components/Timeline/get-sequence-context-menu-items.js +2 -19
  128. package/dist/components/Timeline/get-timeline-sequence-visible-layout.d.ts +6 -1
  129. package/dist/components/Timeline/get-timeline-sequence-visible-layout.js +11 -4
  130. package/dist/components/Timeline/split-selected-timeline-item.js +0 -1
  131. package/dist/components/Timeline/timeline-field-row-layout.d.ts +1 -1
  132. package/dist/components/Timeline/timeline-field-row-layout.js +4 -7
  133. package/dist/components/Timeline/timeline-field-utils.js +0 -2
  134. package/dist/components/Timeline/use-timeline-asset-drop.js +1 -0
  135. package/dist/components/TimelineCombobox.js +25 -160
  136. package/dist/components/TimelineInOutToggle.js +4 -1
  137. package/dist/components/duplicate-jsx-node-api.d.ts +2 -0
  138. package/dist/components/duplicate-jsx-node-api.js +12 -0
  139. package/dist/components/effect-drag-and-drop.js +1 -4
  140. package/dist/components/handle-drop.d.ts +2 -1
  141. package/dist/components/handle-drop.js +3 -3
  142. package/dist/components/import-assets.js +9 -65
  143. package/dist/components/keyboard-shortcuts.d.ts +10 -0
  144. package/dist/components/keyboard-shortcuts.js +135 -0
  145. package/dist/components/selected-outline-control-layout.js +2 -6
  146. package/dist/components/selected-outline-measurement.d.ts +1 -1
  147. package/dist/components/selected-outline-measurement.js +16 -3
  148. package/dist/components/use-delete-asset.d.ts +1 -0
  149. package/dist/components/use-delete-asset.js +40 -0
  150. package/dist/components/use-selected-outline-control-target.d.ts +11 -0
  151. package/dist/components/use-selected-outline-control-target.js +79 -0
  152. package/dist/esm/{chunk-s6k99gh2.js → chunk-4bxz1d3g.js} +3 -2
  153. package/dist/esm/{chunk-v5a8vpv4.js → chunk-w4244j4n.js} +17766 -17618
  154. package/dist/esm/index.mjs +3 -0
  155. package/dist/esm/internals.mjs +17765 -17617
  156. package/dist/esm/previewEntry.mjs +17570 -17422
  157. package/dist/esm/renderEntry.mjs +2 -2
  158. package/dist/helpers/add-asset-cache-bust.d.ts +4 -0
  159. package/dist/helpers/add-asset-cache-bust.js +10 -0
  160. package/dist/helpers/calculate-timeline.js +2 -1
  161. package/dist/helpers/colors.d.ts +2 -0
  162. package/dist/helpers/colors.js +6 -2
  163. package/dist/helpers/drag-aware-double-click.d.ts +6 -0
  164. package/dist/helpers/drag-aware-double-click.js +20 -0
  165. package/dist/helpers/get-asset-metadata.js +25 -16
  166. package/dist/helpers/get-sequence-double-click-action.d.ts +2 -1
  167. package/dist/helpers/get-sequence-double-click-action.js +7 -1
  168. package/dist/helpers/get-timeline-sequence-layout.d.ts +4 -1
  169. package/dist/helpers/get-timeline-sequence-layout.js +15 -6
  170. package/dist/helpers/get-timeline-sequence-sort-key.d.ts +2 -1
  171. package/dist/helpers/hoverable.d.ts +1 -0
  172. package/dist/helpers/hoverable.js +2 -1
  173. package/dist/helpers/inject-css.js +3 -1
  174. package/dist/helpers/open-in-remotion-convert.d.ts +7 -0
  175. package/dist/helpers/open-in-remotion-convert.js +18 -0
  176. package/dist/helpers/use-media-metadata.d.ts +2 -1
  177. package/dist/helpers/use-media-metadata.js +12 -5
  178. package/dist/helpers/use-menu-structure.js +32 -32
  179. package/dist/icons/align-center-horizontal.js +1 -1
  180. package/dist/icons/align-left.js +1 -1
  181. package/dist/icons/align-right.js +1 -1
  182. package/dist/icons/keyboard.d.ts +4 -0
  183. package/dist/icons/keyboard.js +10 -0
  184. package/dist/icons/license.d.ts +4 -0
  185. package/dist/icons/license.js +8 -0
  186. package/dist/icons/package.d.ts +4 -0
  187. package/dist/icons/package.js +8 -0
  188. package/dist/icons/react.js +1 -1
  189. package/dist/icons/remotion-convert.d.ts +4 -0
  190. package/dist/icons/remotion-convert.js +10 -0
  191. package/dist/icons/skills.d.ts +4 -0
  192. package/dist/icons/skills.js +10 -0
  193. package/dist/state/modals.d.ts +2 -5
  194. package/package.json +12 -12
  195. package/dist/components/KeyboardShortcutsExplainer.d.ts +0 -2
  196. package/dist/components/KeyboardShortcutsExplainer.js +0 -194
@@ -33,17 +33,17 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.SelectedOutlinePolygon = void 0;
36
+ exports.SelectedOutlinePolygon = exports.SELECTED_OUTLINE_KEY_ATTR = void 0;
37
37
  const jsx_runtime_1 = require("react/jsx-runtime");
38
38
  const react_1 = __importStar(require("react"));
39
39
  const remotion_1 = require("remotion");
40
40
  const client_id_1 = require("../helpers/client-id");
41
41
  const colors_1 = require("../helpers/colors");
42
+ const drag_aware_double_click_1 = require("../helpers/drag-aware-double-click");
42
43
  const interactivity_enabled_1 = require("../helpers/interactivity-enabled");
43
44
  const pointer_session_1 = require("../helpers/pointer-session");
44
45
  const editor_guides_1 = require("../state/editor-guides");
45
46
  const editor_snapping_1 = require("../state/editor-snapping");
46
- const ContextMenu_1 = require("./ContextMenu");
47
47
  const effect_drag_and_drop_1 = require("./effect-drag-and-drop");
48
48
  const ForceSpecificCursor_1 = require("./ForceSpecificCursor");
49
49
  const NotificationCenter_1 = require("./Notifications/NotificationCenter");
@@ -56,8 +56,10 @@ const focus_inspector_field_1 = require("./Timeline/focus-inspector-field");
56
56
  const imperative_state_1 = require("./Timeline/imperative-state");
57
57
  const save_sequence_prop_1 = require("./Timeline/save-sequence-prop");
58
58
  const should_clear_selection_on_pointer_down_1 = require("./Timeline/should-clear-selection-on-pointer-down");
59
- const SelectedOutlinePolygonUnmemoized = ({ compositionHeight, compositionWidth, containsSelection, directlySelected, dragging, getAllDragOutlines, getAllDragTargets, getLayoutTarget, getTarget, hasTarget, hovered, onContextMenuOpen, onContextMenuOpenChange, outline, onDraggingChange, onHoverChange, onSnapPointsChange, onSelect, onDoubleClickTarget, scale, showSelectedOutline, }) => {
59
+ exports.SELECTED_OUTLINE_KEY_ATTR = 'data-remotion-studio-selected-outline-key';
60
+ const SelectedOutlinePolygonUnmemoized = ({ compositionHeight, compositionWidth, containsSelection, directlySelected, dragging, getAllDragOutlines, getAllDragTargets, getLayoutTarget, getTarget, hasTarget, hovered, outline, onDraggingChange, onHoverChange, onSnapPointsChange, onSelect, onDoubleClickTarget, scale, showSelectedOutline, }) => {
60
61
  const { previewServerState } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
62
+ const dragAwareDoubleClick = (0, react_1.useMemo)(() => (0, drag_aware_double_click_1.createDragAwareDoubleClickTracker)(), []);
61
63
  const { canvasContent } = (0, react_1.useContext)(remotion_1.Internals.CompositionManager);
62
64
  const { getDragOverrides } = (0, react_1.useContext)(remotion_1.Internals.VisualModeDragOverridesContext);
63
65
  const { setPropStatuses, setDragOverrides, clearDragOverrides } = (0, react_1.useContext)(remotion_1.Internals.VisualModeSettersContext);
@@ -254,6 +256,7 @@ const SelectedOutlinePolygonUnmemoized = ({ compositionHeight, compositionWidth,
254
256
  updateDragOverrides();
255
257
  };
256
258
  const onPointerUp = (reason) => {
259
+ dragAwareDoubleClick.endPointerGesture(dragStarted);
257
260
  window.removeEventListener('keydown', onKeyChange);
258
261
  window.removeEventListener('keyup', onKeyChange);
259
262
  if (dragStarted) {
@@ -318,6 +321,7 @@ const SelectedOutlinePolygonUnmemoized = ({ compositionHeight, compositionWidth,
318
321
  compositionHeight,
319
322
  compositionWidth,
320
323
  containsSelection,
324
+ dragAwareDoubleClick,
321
325
  editorShowGuides,
322
326
  editorSnapping,
323
327
  getAllDragOutlines,
@@ -338,12 +342,12 @@ const SelectedOutlinePolygonUnmemoized = ({ compositionHeight, compositionWidth,
338
342
  if (target === undefined) {
339
343
  return;
340
344
  }
341
- if (!onDoubleClickTarget(target, event.button)) {
345
+ if (!onDoubleClickTarget(target, event.button, dragAwareDoubleClick.consumePointerGestureWasDragged())) {
342
346
  return;
343
347
  }
344
348
  event.preventDefault();
345
349
  event.stopPropagation();
346
- }, [getTarget, onDoubleClickTarget]);
350
+ }, [dragAwareDoubleClick, getTarget, onDoubleClickTarget]);
347
351
  const onEffectDragOver = react_1.default.useCallback((event) => {
348
352
  if (!(0, effect_drag_and_drop_1.hasEffectDragType)(event.dataTransfer)) {
349
353
  return;
@@ -391,16 +395,14 @@ const SelectedOutlinePolygonUnmemoized = ({ compositionHeight, compositionWidth,
391
395
  clientId: effectDrop.clientId,
392
396
  });
393
397
  }, [getEffectDropTarget]);
394
- return (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
395
- jsx_runtime_1.jsx("polygon", { ref: polygonRef, [should_clear_selection_on_pointer_down_1.PREVENT_CLEAR_SELECTION_ON_POINTER_DOWN_ATTR]: 'true', "data-remotion-directly-selected-outline": directlySelected ? 'true' : undefined, points: points, fill: effectDropHovered ? colors_1.TIMELINE_DROP_BLUE_ALPHA_12 : colors_1.TRANSPARENT, stroke: colors_1.BLUE, strokeOpacity: visible || effectDropHovered ? 1 : 0, strokeWidth: 2, vectorEffect: "non-scaling-stroke", pointerEvents: hasTarget ? 'all' : undefined, onPointerEnter: () => {
396
- if (!dragging) {
397
- onHoverChange(outline.key);
398
- }
399
- }, onPointerLeave: () => {
400
- if (!dragging) {
401
- onHoverChange(null);
402
- }
403
- }, onPointerDown: onPointerDown, onDoubleClick: onDoubleClick, onDragOver: onEffectDragOver, onDragLeave: onEffectDragLeave, onDrop: onEffectDrop }), jsx_runtime_1.jsx(ContextMenu_1.ContextMenuForTarget, { triggerRef: polygonRef, getItems: onContextMenuOpen, onOpenChange: onContextMenuOpenChange })
404
- ] }));
398
+ return (jsx_runtime_1.jsx("polygon", { ref: polygonRef, [should_clear_selection_on_pointer_down_1.PREVENT_CLEAR_SELECTION_ON_POINTER_DOWN_ATTR]: 'true', [exports.SELECTED_OUTLINE_KEY_ATTR]: outline.key, "data-remotion-directly-selected-outline": directlySelected ? 'true' : undefined, points: points, fill: effectDropHovered ? colors_1.TIMELINE_DROP_BLUE_ALPHA_12 : colors_1.TRANSPARENT, stroke: colors_1.BLUE, strokeOpacity: visible || effectDropHovered ? 1 : 0, strokeWidth: 2, vectorEffect: "non-scaling-stroke", pointerEvents: hasTarget ? 'all' : undefined, onPointerEnter: () => {
399
+ if (!dragging) {
400
+ onHoverChange(outline.key);
401
+ }
402
+ }, onPointerLeave: () => {
403
+ if (!dragging) {
404
+ onHoverChange(null);
405
+ }
406
+ }, onPointerDown: onPointerDown, onPointerDownCapture: dragAwareDoubleClick.beginPointerGesture, onDoubleClick: onDoubleClick, onDragOver: onEffectDragOver, onDragLeave: onEffectDragLeave, onDrop: onEffectDrop }));
405
407
  };
406
408
  exports.SelectedOutlinePolygon = react_1.default.memo(SelectedOutlinePolygonUnmemoized);
@@ -36,10 +36,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.SelectedOutlineRenderer = void 0;
37
37
  const jsx_runtime_1 = require("react/jsx-runtime");
38
38
  const react_1 = __importStar(require("react"));
39
+ const timeline_node_path_key_1 = require("../helpers/timeline-node-path-key");
39
40
  const timeline_sequence_hover_1 = require("../state/timeline-sequence-hover");
41
+ const ContextMenu_1 = require("./ContextMenu");
40
42
  const selected_outline_measurement_1 = require("./selected-outline-measurement");
41
43
  const selected_outline_order_1 = require("./selected-outline-order");
44
+ const SelectedOutlineEditingHandles_1 = require("./SelectedOutlineEditingHandles");
42
45
  const SelectedOutlineElement_1 = require("./SelectedOutlineElement");
46
+ const SelectedOutlinePolygon_1 = require("./SelectedOutlinePolygon");
43
47
  const SelectedOutlineSnapIndicators_1 = require("./SelectedOutlineSnapIndicators");
44
48
  const SelectedOutlineTransformOriginHandle_1 = require("./SelectedOutlineTransformOriginHandle");
45
49
  const SelectedOutlineUvControls_1 = require("./SelectedOutlineUvControls");
@@ -53,30 +57,47 @@ const emptyRenderState = {
53
57
  outlines: [],
54
58
  targets: [],
55
59
  };
56
- const SelectedOutlineHoverCleanup = ({ targets }) => {
57
- const hoveredSequence = (0, timeline_sequence_hover_1.useTimelineSequenceHoverState)();
58
- const setHoveredSequence = (0, timeline_sequence_hover_1.useSetTimelineSequenceHover)();
59
- (0, react_1.useEffect)(() => {
60
- if ((hoveredSequence === null || hoveredSequence === void 0 ? void 0 : hoveredSequence.source) === 'canvas' &&
61
- !targets.some((target) => target.key === hoveredSequence.key)) {
62
- setHoveredSequence((currentHover) => (currentHover === null || currentHover === void 0 ? void 0 : currentHover.source) === 'canvas' ? null : currentHover);
63
- }
64
- }, [hoveredSequence, setHoveredSequence, targets]);
65
- return null;
66
- };
67
60
  const SelectedOutlineRendererUnmemoized = ({ compositionHeight, compositionWidth, dragging, getLatestOutlineTargetByKey, getOutlineTargets, onDraggingChange, onContextMenuOpenChange, onSelect, scale, sequences, updateOutlinesRef, }) => {
61
+ var _a;
68
62
  const [renderState, setRenderState] = (0, react_1.useState)(emptyRenderState);
69
63
  const overlayRef = (0, react_1.useRef)(null);
70
64
  const resizeObserverRef = (0, react_1.useRef)(null);
71
65
  const resizeObserverAnimationFrameRef = (0, react_1.useRef)(null);
72
66
  const observedOutlineElementsRef = (0, react_1.useRef)(new Set());
67
+ const contextMenuOpenHandlersRef = (0, react_1.useRef)(new Map());
68
+ const registerContextMenuOpen = (0, react_1.useCallback)((key, handler) => {
69
+ if (handler === null) {
70
+ contextMenuOpenHandlersRef.current.delete(key);
71
+ }
72
+ else {
73
+ contextMenuOpenHandlersRef.current.set(key, handler);
74
+ }
75
+ }, []);
76
+ const getContextMenuOpenByKey = (0, react_1.useCallback)((key) => contextMenuOpenHandlersRef.current.get(key), []);
77
+ const getDelegatedContextMenuItems = (0, react_1.useCallback)((event) => {
78
+ var _a;
79
+ var _b;
80
+ if (!(event.target instanceof Element)) {
81
+ return false;
82
+ }
83
+ const polygon = event.target.closest(`polygon[${SelectedOutlinePolygon_1.SELECTED_OUTLINE_KEY_ATTR}]`);
84
+ if ((polygon === null || polygon === void 0 ? void 0 : polygon.ownerSVGElement) !== overlayRef.current) {
85
+ return false;
86
+ }
87
+ const key = polygon.getAttribute(SelectedOutlinePolygon_1.SELECTED_OUTLINE_KEY_ATTR);
88
+ return key === null ? false : ((_b = (_a = getContextMenuOpenByKey(key)) === null || _a === void 0 ? void 0 : _a()) !== null && _b !== void 0 ? _b : false);
89
+ }, [getContextMenuOpenByKey]);
90
+ const hoveredSequence = (0, timeline_sequence_hover_1.useTimelineSequenceHoverState)();
91
+ const setHoveredSequence = (0, timeline_sequence_hover_1.useSetTimelineSequenceHover)();
92
+ const hoveredNodePathKey = (_a = hoveredSequence === null || hoveredSequence === void 0 ? void 0 : hoveredSequence.nodePathKey) !== null && _a !== void 0 ? _a : null;
93
+ const hoveredTimelineNodePathKey = (hoveredSequence === null || hoveredSequence === void 0 ? void 0 : hoveredSequence.source) === 'timeline' ? hoveredNodePathKey : null;
73
94
  const updateOutlines = (0, react_1.useCallback)(() => {
74
95
  const targets = getOutlineTargets();
75
96
  if (overlayRef.current === null || targets.length === 0) {
76
97
  setRenderState((prevState) => prevState.targets.length === 0 ? prevState : emptyRenderState);
77
98
  return;
78
99
  }
79
- const nextOutlines = (0, selected_outline_measurement_1.measureOutlines)(overlayRef.current, targets);
100
+ const nextOutlines = (0, selected_outline_measurement_1.measureOutlines)(overlayRef.current, targets, hoveredTimelineNodePathKey);
80
101
  setRenderState((prevState) => {
81
102
  const outlines = (0, selected_outline_measurement_1.outlinesAreEqual)(prevState.outlines, nextOutlines)
82
103
  ? prevState.outlines
@@ -86,7 +107,7 @@ const SelectedOutlineRendererUnmemoized = ({ compositionHeight, compositionWidth
86
107
  }
87
108
  return { outlines, targets };
88
109
  });
89
- }, [getOutlineTargets]);
110
+ }, [getOutlineTargets, hoveredTimelineNodePathKey]);
90
111
  (0, react_1.useLayoutEffect)(() => {
91
112
  updateOutlinesRef.current = updateOutlines;
92
113
  updateOutlines();
@@ -149,6 +170,12 @@ const SelectedOutlineRendererUnmemoized = ({ compositionHeight, compositionWidth
149
170
  const targetsByKey = (0, react_1.useMemo)(() => {
150
171
  return new Map(renderState.targets.map((target) => [target.key, target]));
151
172
  }, [renderState.targets]);
173
+ (0, react_1.useEffect)(() => {
174
+ if ((hoveredSequence === null || hoveredSequence === void 0 ? void 0 : hoveredSequence.source) === 'canvas' &&
175
+ !renderState.targets.some((target) => target.key === hoveredSequence.key)) {
176
+ setHoveredSequence((currentHover) => (currentHover === null || currentHover === void 0 ? void 0 : currentHover.source) === 'canvas' ? null : currentHover);
177
+ }
178
+ }, [hoveredSequence, renderState.targets, setHoveredSequence]);
152
179
  // Reordering a captured SVG target can cancel the active pointer session.
153
180
  const outlineRenderingOrderRef = (0, react_1.useRef)([]);
154
181
  const outlinesForRendering = (0, react_1.useMemo)(() => {
@@ -176,6 +203,31 @@ const SelectedOutlineRendererUnmemoized = ({ compositionHeight, compositionWidth
176
203
  const outlinesByKey = (0, react_1.useMemo)(() => {
177
204
  return new Map(renderState.outlines.map((outline) => [outline.key, outline]));
178
205
  }, [renderState.outlines]);
206
+ const { outlinesForEditingHandles, outlinesForTransformOrigin, outlinesForUvHandles, } = (0, react_1.useMemo)(() => {
207
+ const editingHandles = [];
208
+ const transformOrigin = [];
209
+ const uvHandles = [];
210
+ for (const outline of outlinesForRendering) {
211
+ const target = targetsByKey.get(outline.key);
212
+ if ((target === null || target === void 0 ? void 0 : target.containsSelection) === true ||
213
+ (target !== undefined &&
214
+ (0, timeline_node_path_key_1.timelineSequenceNodePathToKey)(target.nodePathInfo.sequenceSubscriptionKey) === hoveredNodePathKey)) {
215
+ editingHandles.push(outline);
216
+ }
217
+ if ((target === null || target === void 0 ? void 0 : target.selectedForUvHandles) === true) {
218
+ uvHandles.push(outline);
219
+ }
220
+ if ((target === null || target === void 0 ? void 0 : target.selectedForTransformOrigin) === true ||
221
+ (target === null || target === void 0 ? void 0 : target.selectedForRotation) === true) {
222
+ transformOrigin.push(outline);
223
+ }
224
+ }
225
+ return {
226
+ outlinesForEditingHandles: editingHandles,
227
+ outlinesForTransformOrigin: transformOrigin,
228
+ outlinesForUvHandles: uvHandles,
229
+ };
230
+ }, [hoveredNodePathKey, outlinesForRendering, targetsByKey]);
179
231
  const targetsRef = (0, react_1.useRef)(renderState.targets);
180
232
  const outlinesByKeyRef = (0, react_1.useRef)(outlinesByKey);
181
233
  (0, react_1.useLayoutEffect)(() => {
@@ -220,6 +272,6 @@ const SelectedOutlineRendererUnmemoized = ({ compositionHeight, compositionWidth
220
272
  const updateSnapPointsRef = (0, react_1.useRef)(() => undefined);
221
273
  const onSnapPointsChange = (0, react_1.useCallback)((snapPoints) => updateSnapPointsRef.current(snapPoints), []);
222
274
  return (jsx_runtime_1.jsxs("svg", { ref: overlayRef, style: outlineContainer, width: "100%", height: "100%", "aria-hidden": "true", children: [
223
- jsx_runtime_1.jsx(SelectedOutlineHoverCleanup, { targets: renderState.targets }), jsx_runtime_1.jsx(SelectedOutlineSnapIndicators_1.SelectedOutlineSnapIndicators, { compositionHeight: compositionHeight, compositionWidth: compositionWidth, scale: scale, updateSnapPointsRef: updateSnapPointsRef }), outlinesForRendering.map((outline) => (jsx_runtime_1.jsx(SelectedOutlineElement_1.SelectedOutlineElement, { compositionHeight: compositionHeight, compositionWidth: compositionWidth, dragging: dragging, getAllDragOutlines: getAllDragOutlines, getAllDragTargets: getAllDragTargets, getAllRotationDragTargets: getAllRotationDragTargets, getAllScaleDragTargets: getAllScaleDragTargets, getLatestTargetByKey: getLatestOutlineTargetByKey, outline: outline, onDraggingChange: onDraggingChange, onContextMenuOpenChange: onContextMenuOpenChange, onSnapPointsChange: onSnapPointsChange, onSelect: onSelect, scale: scale, layoutTarget: targetsByKey.get(outline.key) }, outline.key))), outlinesForRendering.map((outline) => (jsx_runtime_1.jsx(SelectedOutlineUvControls_1.SelectedOutlineUvHandleConnectionLayer, { outline: outline, layoutTarget: targetsByKey.get(outline.key) }, `${outline.key}-uv-connection-lines`))), outlinesForRendering.map((outline) => (jsx_runtime_1.jsx(SelectedOutlineUvControls_1.SelectedOutlineUvHandleCircleLayer, { onDraggingChange: onDraggingChange, onSelect: onSelect, outline: outline, layoutTarget: targetsByKey.get(outline.key) }, `${outline.key}-uv-handles`))), outlinesForRendering.map((outline) => (jsx_runtime_1.jsx(SelectedOutlineTransformOriginHandle_1.SelectedOutlineTransformOriginHandle, { outline: outline, onDraggingChange: onDraggingChange, getLatestTargetByKey: getLatestOutlineTargetByKey, layoutTarget: targetsByKey.get(outline.key) }, `${outline.key}-transform-origin`)))] }));
275
+ jsx_runtime_1.jsx(ContextMenu_1.ContextMenuForTarget, { triggerRef: overlayRef, getItems: getDelegatedContextMenuItems, onOpenChange: onContextMenuOpenChange }), jsx_runtime_1.jsx(SelectedOutlineSnapIndicators_1.SelectedOutlineSnapIndicators, { compositionHeight: compositionHeight, compositionWidth: compositionWidth, scale: scale, updateSnapPointsRef: updateSnapPointsRef }), outlinesForRendering.map((outline) => (jsx_runtime_1.jsx(SelectedOutlineElement_1.SelectedOutlineElement, { compositionHeight: compositionHeight, compositionWidth: compositionWidth, dragging: dragging, getAllDragOutlines: getAllDragOutlines, getAllDragTargets: getAllDragTargets, getLatestTargetByKey: getLatestOutlineTargetByKey, outline: outline, onDraggingChange: onDraggingChange, onSnapPointsChange: onSnapPointsChange, onSelect: onSelect, registerContextMenuOpen: registerContextMenuOpen, scale: scale, layoutTarget: targetsByKey.get(outline.key) }, outline.key))), outlinesForEditingHandles.map((outline) => (jsx_runtime_1.jsx(SelectedOutlineEditingHandles_1.SelectedOutlineEditingHandles, { dragging: dragging, getAllRotationDragTargets: getAllRotationDragTargets, getAllScaleDragTargets: getAllScaleDragTargets, getContextMenuOpenByKey: getContextMenuOpenByKey, getLatestTargetByKey: getLatestOutlineTargetByKey, layoutTarget: targetsByKey.get(outline.key), onContextMenuOpenChange: onContextMenuOpenChange, onDraggingChange: onDraggingChange, onSelect: onSelect, outline: outline }, `${outline.key}-editing-handles`))), outlinesForUvHandles.map((outline) => (jsx_runtime_1.jsx(SelectedOutlineUvControls_1.SelectedOutlineUvHandleConnectionLayer, { outline: outline, layoutTarget: targetsByKey.get(outline.key) }, `${outline.key}-uv-connection-lines`))), outlinesForUvHandles.map((outline) => (jsx_runtime_1.jsx(SelectedOutlineUvControls_1.SelectedOutlineUvHandleCircleLayer, { onDraggingChange: onDraggingChange, onSelect: onSelect, outline: outline, layoutTarget: targetsByKey.get(outline.key) }, `${outline.key}-uv-handles`))), outlinesForTransformOrigin.map((outline) => (jsx_runtime_1.jsx(SelectedOutlineTransformOriginHandle_1.SelectedOutlineTransformOriginHandle, { outline: outline, onDraggingChange: onDraggingChange, getLatestTargetByKey: getLatestOutlineTargetByKey, layoutTarget: targetsByKey.get(outline.key) }, `${outline.key}-transform-origin`)))] }));
224
276
  };
225
277
  exports.SelectedOutlineRenderer = react_1.default.memo(SelectedOutlineRendererUnmemoized);
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- type SettingsTab = 'apps' | 'rendering' | 'studio' | 'license';
2
+ type SettingsTab = 'apps' | 'rendering' | 'studio' | 'packages' | 'shortcuts' | 'skills' | 'license';
3
3
  export declare const SettingsModal: React.FC<{
4
4
  readonly initialTab: SettingsTab;
5
5
  readonly initialPublicLicenseKey: string | null;
@@ -3,13 +3,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SettingsModal = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
+ const browser_studio_operations_1 = require("../helpers/browser-studio-operations");
6
7
  const apps_1 = require("../icons/apps");
7
- const certificate_1 = require("../icons/certificate");
8
+ const keyboard_1 = require("../icons/keyboard");
9
+ const license_1 = require("../icons/license");
10
+ const package_1 = require("../icons/package");
8
11
  const remotion_triangle_1 = require("../icons/remotion-triangle");
12
+ const skills_1 = require("../icons/skills");
9
13
  const video_1 = require("../icons/video");
10
14
  const modals_1 = require("../state/modals");
11
15
  const ConfigureDefaultEditorModal_1 = require("./ConfigureDefaultEditorModal");
12
16
  const ConfigureLicenseModal_1 = require("./ConfigureLicenseModal");
17
+ const InstallPackage_1 = require("./InstallPackage");
18
+ const KeyboardShortcutsSettings_1 = require("./KeyboardShortcutsSettings");
13
19
  const is_menu_item_1 = require("./Menu/is-menu-item");
14
20
  const ModalHeader_1 = require("./ModalHeader");
15
21
  const DismissableModal_1 = require("./NewComposition/DismissableModal");
@@ -17,6 +23,7 @@ const RenderingSettings_1 = require("./RenderingSettings");
17
23
  const render_modals_1 = require("./RenderModal/render-modals");
18
24
  const SettingsContext_1 = require("./SettingsContext");
19
25
  const SettingsModalFooter_1 = require("./SettingsModalFooter");
26
+ const SkillsSettings_1 = require("./SkillsSettings");
20
27
  const StudioSettings_1 = require("./StudioSettings");
21
28
  const vertical_1 = require("./Tabs/vertical");
22
29
  const hiddenPanel = {
@@ -27,21 +34,38 @@ const settingsOptionsPanel = {
27
34
  boxSizing: 'border-box',
28
35
  paddingBottom: 16,
29
36
  };
37
+ const settingsLeftSidebar = {
38
+ ...render_modals_1.leftSidebar,
39
+ paddingLeft: 8,
40
+ };
30
41
  const appsIcon = {
31
42
  ...render_modals_1.icon,
32
- height: 20,
33
- width: 20,
43
+ flexShrink: 0,
44
+ height: 24,
45
+ width: 24,
34
46
  };
35
- const appsIconContainer = {
36
- ...render_modals_1.iconContainer,
47
+ const skillsIcon = {
48
+ ...render_modals_1.icon,
49
+ flexShrink: 0,
37
50
  height: 20,
38
51
  width: 20,
39
52
  };
53
+ const keyboardIcon = {
54
+ ...render_modals_1.icon,
55
+ height: 16,
56
+ width: 16,
57
+ };
40
58
  const SettingsModal = ({ initialPublicLicenseKey, initialTab }) => {
41
59
  const { setSelectedModal } = (0, react_1.useContext)(modals_1.SetSelectedModalContext);
42
60
  const { setPublicLicenseKey } = (0, SettingsContext_1.useSettings)();
61
+ const isBrowserStudio = (0, browser_studio_operations_1.getBrowserStudioOperations)() !== null;
62
+ const packageManager = window.remotion_packageManager === 'unknown'
63
+ ? null
64
+ : window.remotion_packageManager;
65
+ const showPackages = (0, browser_studio_operations_1.canInstallPackages)() && (isBrowserStudio || packageManager !== null);
43
66
  const [tab, setTab] = (0, react_1.useState)(initialTab);
44
67
  const [openedTabs, setOpenedTabs] = (0, react_1.useState)([initialTab]);
68
+ const [packagesFooterContainer, setPackagesFooterContainer] = (0, react_1.useState)(null);
45
69
  const dismiss = (0, react_1.useCallback)(() => {
46
70
  setSelectedModal(null);
47
71
  }, [setSelectedModal]);
@@ -59,7 +83,6 @@ const SettingsModal = ({ initialPublicLicenseKey, initialTab }) => {
59
83
  }, [initialPublicLicenseKey, setPublicLicenseKey]);
60
84
  return (jsx_runtime_1.jsx(DismissableModal_1.DismissableModal, { panelStyle: render_modals_1.outerModalStyle, children: jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
61
85
  jsx_runtime_1.jsx(ModalHeader_1.ModalHeader, { title: "Settings", onClose: dismiss }), jsx_runtime_1.jsxs("div", { style: render_modals_1.horizontalLayout, children: [
62
- jsx_runtime_1.jsxs("div", { style: render_modals_1.leftSidebar, children: [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 === '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" }), 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: appsIconContainer, children: jsx_runtime_1.jsx(apps_1.AppsIcon, { color: color, style: appsIcon }) })), children: "Apps" }), 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(certificate_1.CertificateIcon, { color: color, style: render_modals_1.icon }) })), children: "License" })] }), 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('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('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] }), jsx_runtime_1.jsx(SettingsModalFooter_1.SettingsModalFooter, {})
63
- ] }) }));
86
+ jsx_runtime_1.jsxs("div", { style: settingsLeftSidebar, children: [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" })), 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" })), 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" })), 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" }))] }), 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('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] }), tab === 'packages' ? (jsx_runtime_1.jsx("div", { ref: setPackagesFooterContainer })) : isBrowserStudio ? null : (jsx_runtime_1.jsx(SettingsModalFooter_1.SettingsModalFooter, {}))] }) }));
64
87
  };
65
88
  exports.SettingsModal = SettingsModal;
@@ -5,6 +5,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const colors_1 = require("../helpers/colors");
7
7
  const InspectorOpenInEditor_1 = require("./InspectorOpenInEditor");
8
+ const layout_1 = require("./layout");
8
9
  const ModalFooter_1 = require("./ModalFooter");
9
10
  const footer = {
10
11
  flex: 'none',
@@ -38,7 +39,7 @@ const SettingsModalFooter = () => {
38
39
  };
39
40
  }, []);
40
41
  return (jsx_runtime_1.jsx(ModalFooter_1.ModalFooterContainer, { style: footer, children: jsx_runtime_1.jsx("div", { style: footerRow, children: jsx_runtime_1.jsxs("div", { style: configFileHint, children: ["Changes save to",
41
- jsx_runtime_1.jsx(InspectorOpenInEditor_1.InspectorOpenInEditor, { locationType: null, location: configFileLocation, label: jsx_runtime_1.jsx("strong", { style: configFileName, children: "remotion.config.ts" }) })
42
+ 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" }) })
42
43
  ] }) }) }));
43
44
  };
44
45
  exports.SettingsModalFooter = SettingsModalFooter;
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const SkillsSettings: React.FC;
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SkillsSettings = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const colors_1 = require("../helpers/colors");
7
+ const copy_text_1 = require("../helpers/copy-text");
8
+ const check_circle_filled_1 = require("../icons/check-circle-filled");
9
+ const clipboard_1 = require("../icons/clipboard");
10
+ const minus_1 = require("../icons/minus");
11
+ const InlineAction_1 = require("./InlineAction");
12
+ const ValidationMessage_1 = require("./NewComposition/ValidationMessage");
13
+ const NotificationCenter_1 = require("./Notifications/NotificationCenter");
14
+ const SettingsContext_1 = require("./SettingsContext");
15
+ const INSTALL_COMMAND = 'npx remotion skills add';
16
+ const container = {
17
+ alignSelf: 'flex-start',
18
+ boxSizing: 'border-box',
19
+ flex: 1,
20
+ fontFamily: 'sans-serif',
21
+ minWidth: 0,
22
+ padding: '16px 16px 0',
23
+ };
24
+ const description = {
25
+ color: colors_1.LIGHT_TEXT,
26
+ fontSize: 13,
27
+ lineHeight: 1.5,
28
+ margin: 0,
29
+ };
30
+ const commandField = {
31
+ alignItems: 'center',
32
+ backgroundColor: 'rgba(0, 0, 0, 0.22)',
33
+ borderRadius: 4,
34
+ display: 'flex',
35
+ marginTop: 8,
36
+ padding: '5px 6px 5px 9px',
37
+ };
38
+ const command = {
39
+ color: colors_1.WHITE,
40
+ flex: 1,
41
+ fontFamily: 'monospace',
42
+ fontSize: 13,
43
+ lineHeight: '24px',
44
+ margin: 0,
45
+ minWidth: 0,
46
+ overflowX: 'auto',
47
+ whiteSpace: 'pre',
48
+ };
49
+ const copyIcon = {
50
+ height: 12,
51
+ width: 12,
52
+ };
53
+ const list = {
54
+ marginTop: 14,
55
+ };
56
+ const skillRow = {
57
+ alignItems: 'center',
58
+ borderBottom: colors_1.BORDER_WHITE_ALPHA_12,
59
+ display: 'flex',
60
+ gap: 10,
61
+ minHeight: 38,
62
+ padding: '0 10px',
63
+ };
64
+ const lastSkillRow = {
65
+ ...skillRow,
66
+ borderBottom: 'none',
67
+ };
68
+ const statusIcon = {
69
+ flexShrink: 0,
70
+ height: 14,
71
+ width: 14,
72
+ };
73
+ const skillName = {
74
+ color: colors_1.WHITE,
75
+ flex: 1,
76
+ fontFamily: 'monospace',
77
+ fontSize: 13,
78
+ lineHeight: 1.4,
79
+ minWidth: 0,
80
+ overflow: 'hidden',
81
+ textOverflow: 'ellipsis',
82
+ whiteSpace: 'nowrap',
83
+ };
84
+ const status = {
85
+ color: colors_1.LIGHT_TEXT,
86
+ fontSize: 12,
87
+ lineHeight: 1.4,
88
+ whiteSpace: 'nowrap',
89
+ };
90
+ const loading = {
91
+ ...description,
92
+ marginTop: 14,
93
+ };
94
+ const SkillsSettings = () => {
95
+ var _a;
96
+ const { error, remotionSkillsInfo } = (0, SettingsContext_1.useSettings)();
97
+ const installedSkills = (0, react_1.useMemo)(() => {
98
+ var _a;
99
+ return ((_a = remotionSkillsInfo === null || remotionSkillsInfo === void 0 ? void 0 : remotionSkillsInfo.skills.filter(({ installedGlobally, installedInProject }) => installedGlobally || installedInProject).length) !== null && _a !== void 0 ? _a : 0);
100
+ }, [remotionSkillsInfo]);
101
+ const missingSkills = ((_a = remotionSkillsInfo === null || remotionSkillsInfo === void 0 ? void 0 : remotionSkillsInfo.skills.length) !== null && _a !== void 0 ? _a : 0) - installedSkills;
102
+ const onCopy = (0, react_1.useCallback)(() => {
103
+ (0, copy_text_1.copyText)(INSTALL_COMMAND).catch((err) => {
104
+ (0, NotificationCenter_1.showNotification)(`Could not copy: ${err.message}`, 2000);
105
+ });
106
+ }, []);
107
+ const renderCopyAction = (0, react_1.useCallback)((color) => {
108
+ return jsx_runtime_1.jsx(clipboard_1.ClipboardIcon, { color: color, style: copyIcon });
109
+ }, []);
110
+ 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 ? (jsx_runtime_1.jsxs("div", { children: [
111
+ 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: [
112
+ 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" })
113
+ ] })
114
+ ] })) : null, remotionSkillsInfo ? (jsx_runtime_1.jsx("div", { style: list, role: "list", "aria-label": "Remotion Agent Skills", children: remotionSkillsInfo.skills.map((skill, index) => {
115
+ const installed = skill.installedInProject || skill.installedGlobally;
116
+ const installedLocation = skill.installedInProject && skill.installedGlobally
117
+ ? 'Project and global'
118
+ : skill.installedInProject
119
+ ? 'Project'
120
+ : skill.installedGlobally
121
+ ? 'Global'
122
+ : 'Not installed';
123
+ return (jsx_runtime_1.jsxs("div", { role: "listitem", style: index === remotionSkillsInfo.skills.length - 1
124
+ ? lastSkillRow
125
+ : skillRow, children: [installed ? (jsx_runtime_1.jsx(check_circle_filled_1.CheckCircleFilled, { "aria-hidden": true, style: { ...statusIcon, fill: colors_1.BLUE } })) : (jsx_runtime_1.jsx(minus_1.Minus, { "aria-hidden": true, color: colors_1.LIGHT_TEXT, style: statusIcon })), jsx_runtime_1.jsxs("span", { style: skillName, children: ["/", skill.name] }), jsx_runtime_1.jsx("span", { style: status, children: installedLocation })
126
+ ] }, skill.name));
127
+ }) })) : null] }));
128
+ };
129
+ exports.SkillsSettings = SkillsSettings;
@@ -4,6 +4,7 @@ exports.StaticFilePreview = 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
+ const add_asset_cache_bust_1 = require("../helpers/add-asset-cache-bust");
7
8
  const client_id_1 = require("../helpers/client-id");
8
9
  const colors_1 = require("../helpers/colors");
9
10
  const get_preview_file_type_1 = require("../helpers/get-preview-file-type");
@@ -36,6 +37,11 @@ const StaticFilePreview = ({ currentAsset, assetMetadata }) => {
36
37
  if (!currentAsset) {
37
38
  return null;
38
39
  }
39
- return (jsx_runtime_1.jsx(FilePreview_1.FilePreview, { currentAsset: currentAsset, fileType: fileType, src: `${staticFileSrc}?date=${assetMetadata && assetMetadata.type === 'found' ? assetMetadata.fetchedAt : 0}`, assetMetadata: assetMetadata }));
40
+ return (jsx_runtime_1.jsx(FilePreview_1.FilePreview, { currentAsset: currentAsset, fileType: fileType, src: (0, add_asset_cache_bust_1.addAssetCacheBust)({
41
+ fetchedAt: assetMetadata && assetMetadata.type === 'found'
42
+ ? assetMetadata.fetchedAt
43
+ : 0,
44
+ src: staticFileSrc,
45
+ }), assetMetadata: assetMetadata }));
40
46
  };
41
47
  exports.StaticFilePreview = StaticFilePreview;
@@ -6,16 +6,14 @@ const client_1 = require("@remotion/renderer/client");
6
6
  const studio_shared_1 = require("@remotion/studio-shared");
7
7
  const react_1 = require("react");
8
8
  const colors_1 = require("../helpers/colors");
9
- const Checkmark_1 = require("../icons/Checkmark");
10
9
  const undo_1 = require("../icons/undo");
11
10
  const Button_1 = require("./Button");
11
+ const ConfigSelect_1 = require("./ConfigSelect");
12
12
  const styles_1 = require("./InspectorPanel/styles");
13
13
  const layout_1 = require("./layout");
14
- const ComboBox_1 = require("./NewComposition/ComboBox");
15
14
  const InputDragger_1 = require("./NewComposition/InputDragger");
16
15
  const ValidationMessage_1 = require("./NewComposition/ValidationMessage");
17
16
  const layout_2 = require("./RenderModal/layout");
18
- const RenderModalHr_1 = require("./RenderModal/RenderModalHr");
19
17
  const SettingsContext_1 = require("./SettingsContext");
20
18
  const use_auto_save_config_1 = require("./use-auto-save-config");
21
19
  const container = {
@@ -29,15 +27,14 @@ const dividerLabel = {
29
27
  margin: 0,
30
28
  padding: '4px 16px',
31
29
  };
32
- const controlWidth = {
33
- boxSizing: 'border-box',
34
- width: 180,
30
+ const sectionTitle = {
31
+ ...dividerLabel,
32
+ marginTop: 8,
35
33
  };
36
34
  const resetIcon = {
37
35
  height: 12,
38
36
  width: 12,
39
37
  };
40
- const DEFAULT_VALUE = 'studio-default';
41
38
  const initialSettings = {
42
39
  askAIEnabled: null,
43
40
  audioLatencyHint: null,
@@ -50,39 +47,6 @@ const initialSettings = {
50
47
  numberOfSharedAudioTags: null,
51
48
  rspack: null,
52
49
  };
53
- const ConfigSelect = ({ defaultLabel, name, onChange, options, value, }) => {
54
- const values = [
55
- {
56
- id: DEFAULT_VALUE,
57
- keyHint: null,
58
- label: `Default (${defaultLabel})`,
59
- leftItem: value === null ? jsx_runtime_1.jsx(Checkmark_1.Checkmark, {}) : null,
60
- onClick: () => onChange(null),
61
- quickSwitcherLabel: null,
62
- subMenu: null,
63
- type: 'item',
64
- value: DEFAULT_VALUE,
65
- },
66
- { id: `${DEFAULT_VALUE}-divider`, type: 'divider' },
67
- ...options.map((option) => {
68
- const id = `${name}-${String(option.value)}`;
69
- return {
70
- id,
71
- keyHint: null,
72
- label: option.label,
73
- leftItem: value === option.value ? jsx_runtime_1.jsx(Checkmark_1.Checkmark, {}) : null,
74
- onClick: () => onChange(option.value),
75
- quickSwitcherLabel: null,
76
- subMenu: null,
77
- type: 'item',
78
- value: id,
79
- };
80
- }),
81
- ];
82
- return (jsx_runtime_1.jsxs("div", { style: layout_2.optionRow, children: [
83
- jsx_runtime_1.jsx("div", { style: layout_2.label, children: name }), jsx_runtime_1.jsx("div", { style: layout_2.rightRow, children: jsx_runtime_1.jsx(ComboBox_1.Combobox, { values: values, selectedId: value === null ? DEFAULT_VALUE : `${name}-${String(value)}`, style: controlWidth, title: name }) })
84
- ] }));
85
- };
86
50
  const ConfigNumber = ({ defaultLabel, defaultValue, name, onChange, onChangeEnd, value, }) => {
87
51
  const resolvedDefaultLabel = defaultLabel !== null && defaultLabel !== void 0 ? defaultLabel : String(defaultValue);
88
52
  return (jsx_runtime_1.jsxs("div", { style: layout_2.optionRow, children: [
@@ -91,10 +55,6 @@ const ConfigNumber = ({ defaultLabel, defaultValue, name, onChange, onChangeEnd,
91
55
  ] })
92
56
  ] }));
93
57
  };
94
- const booleanOptions = [
95
- { label: 'Enabled', value: true },
96
- { label: 'Disabled', value: false },
97
- ];
98
58
  const StudioSettings = () => {
99
59
  var _a;
100
60
  const { error: settingsError, revision, studioRuntimeConfig } = (0, SettingsContext_1.useSettings)();
@@ -140,7 +100,6 @@ const StudioSettings = () => {
140
100
  update('setAudioLatencyHint', settings.audioLatencyHint),
141
101
  update('setNumberOfSharedAudioTags', committedNumberSettings.numberOfSharedAudioTags),
142
102
  update('setRspack', settings.rspack),
143
- update('setKeyboardShortcutsEnabled', settings.keyboardShortcutsEnabled),
144
103
  update('setInteractivityEnabled', settings.interactivityEnabled),
145
104
  update('setLogLevel', settings.logLevel),
146
105
  ].filter((item) => editedSetters.has(item.setter));
@@ -167,14 +126,14 @@ const StudioSettings = () => {
167
126
  return null;
168
127
  }
169
128
  return (jsx_runtime_1.jsxs("div", { style: container, children: [
170
- jsx_runtime_1.jsx("p", { style: dividerLabel, children: "Interface" }), jsx_runtime_1.jsx(ConfigSelect, { defaultLabel: "Enabled", name: "Ask AI enabled", onChange: (value) => changeSetting('askAIEnabled', 'setAskAIEnabled', value), options: booleanOptions, value: settings.askAIEnabled }), jsx_runtime_1.jsx(ConfigSelect, { defaultLabel: "Enabled", name: "Keyboard shortcuts enabled", onChange: (value) => changeSetting('keyboardShortcutsEnabled', 'setKeyboardShortcutsEnabled', value), options: booleanOptions, value: settings.keyboardShortcutsEnabled }), jsx_runtime_1.jsx(ConfigSelect, { defaultLabel: "Enabled", name: "Interactivity enabled", onChange: (value) => changeSetting('interactivityEnabled', 'setInteractivityEnabled', value), options: booleanOptions, value: settings.interactivityEnabled }), jsx_runtime_1.jsx(ConfigNumber, { defaultLabel: "Unlimited", defaultValue: studio_shared_1.DEFAULT_TIMELINE_TRACKS, name: "Max timeline tracks", onChange: (value) => previewNumberSetting('maxTimelineTracks', value), onChangeEnd: (value) => commitNumberSetting('maxTimelineTracks', 'setMaxTimelineTracks', value), value: settings.maxTimelineTracks }), jsx_runtime_1.jsx(RenderModalHr_1.RenderModalHr, {}), jsx_runtime_1.jsx("p", { style: dividerLabel, children: "Audio" }), jsx_runtime_1.jsx(ConfigSelect, { defaultLabel: "Playback", name: "Audio latency hint", onChange: (value) => changeSetting('audioLatencyHint', 'setAudioLatencyHint', value), options: [
129
+ jsx_runtime_1.jsx("p", { style: dividerLabel, children: "Interface" }), jsx_runtime_1.jsx(ConfigSelect_1.ConfigSelect, { defaultLabel: "Enabled", name: "Ask AI enabled", onChange: (value) => changeSetting('askAIEnabled', 'setAskAIEnabled', value), options: ConfigSelect_1.booleanOptions, value: settings.askAIEnabled }), jsx_runtime_1.jsx(ConfigSelect_1.ConfigSelect, { defaultLabel: "Enabled", name: "Interactivity enabled", onChange: (value) => changeSetting('interactivityEnabled', 'setInteractivityEnabled', value), options: ConfigSelect_1.booleanOptions, value: settings.interactivityEnabled }), jsx_runtime_1.jsx(ConfigNumber, { defaultLabel: "Unlimited", defaultValue: studio_shared_1.DEFAULT_TIMELINE_TRACKS, name: "Max timeline tracks", onChange: (value) => previewNumberSetting('maxTimelineTracks', value), onChangeEnd: (value) => commitNumberSetting('maxTimelineTracks', 'setMaxTimelineTracks', value), value: settings.maxTimelineTracks }), jsx_runtime_1.jsx("p", { style: sectionTitle, children: "Audio" }), jsx_runtime_1.jsx(ConfigSelect_1.ConfigSelect, { defaultLabel: "Playback", name: "Audio latency hint", onChange: (value) => changeSetting('audioLatencyHint', 'setAudioLatencyHint', value), options: [
171
130
  { label: 'Interactive', value: 'interactive' },
172
131
  { label: 'Balanced', value: 'balanced' },
173
132
  { label: 'Playback', value: 'playback' },
174
- ], value: settings.audioLatencyHint }), jsx_runtime_1.jsx(ConfigNumber, { defaultValue: 0, name: "Number of shared audio tags", onChange: (value) => previewNumberSetting('numberOfSharedAudioTags', value), onChangeEnd: (value) => commitNumberSetting('numberOfSharedAudioTags', 'setNumberOfSharedAudioTags', value), value: settings.numberOfSharedAudioTags }), jsx_runtime_1.jsx(ConfigSelect, { defaultLabel: "Disabled", name: "Beep on finish", onChange: (value) => changeSetting('beepOnFinish', 'setBeepOnFinish', value), options: booleanOptions, value: settings.beepOnFinish }), jsx_runtime_1.jsx(RenderModalHr_1.RenderModalHr, {}), jsx_runtime_1.jsx("p", { style: dividerLabel, children: "Development" }), jsx_runtime_1.jsx(ConfigSelect, { defaultLabel: "Webpack", name: "Bundler", onChange: (value) => changeSetting('rspack', 'setRspack', value), options: [
133
+ ], value: settings.audioLatencyHint }), jsx_runtime_1.jsx(ConfigNumber, { defaultValue: 0, name: "Number of shared audio tags", onChange: (value) => previewNumberSetting('numberOfSharedAudioTags', value), onChangeEnd: (value) => commitNumberSetting('numberOfSharedAudioTags', 'setNumberOfSharedAudioTags', value), value: settings.numberOfSharedAudioTags }), jsx_runtime_1.jsx(ConfigSelect_1.ConfigSelect, { defaultLabel: "Disabled", name: "Beep on finish", onChange: (value) => changeSetting('beepOnFinish', 'setBeepOnFinish', value), options: ConfigSelect_1.booleanOptions, value: settings.beepOnFinish }), jsx_runtime_1.jsx("p", { style: sectionTitle, children: "Development" }), jsx_runtime_1.jsx(ConfigSelect_1.ConfigSelect, { defaultLabel: "Webpack", name: "Bundler", onChange: (value) => changeSetting('rspack', 'setRspack', value), options: [
175
134
  { label: 'Rspack', value: true },
176
135
  { label: 'Webpack', value: false },
177
- ], value: settings.rspack }), jsx_runtime_1.jsx(ConfigSelect, { defaultLabel: "Disabled", name: "Cross-site isolation", onChange: (value) => changeSetting('enableCrossSiteIsolation', 'setEnableCrossSiteIsolation', value), options: booleanOptions, value: settings.enableCrossSiteIsolation }), jsx_runtime_1.jsx(ConfigSelect, { defaultLabel: "Info", name: "Log level", onChange: (value) => changeSetting('logLevel', 'setLogLevel', value), options: client_1.BrowserSafeApis.logLevels.map((value) => ({
136
+ ], value: settings.rspack }), jsx_runtime_1.jsx(ConfigSelect_1.ConfigSelect, { defaultLabel: "Disabled", name: "Cross-site isolation", onChange: (value) => changeSetting('enableCrossSiteIsolation', 'setEnableCrossSiteIsolation', value), options: ConfigSelect_1.booleanOptions, value: settings.enableCrossSiteIsolation }), jsx_runtime_1.jsx(ConfigSelect_1.ConfigSelect, { defaultLabel: "Info", name: "Log level", onChange: (value) => changeSetting('logLevel', 'setLogLevel', value), options: client_1.BrowserSafeApis.logLevels.map((value) => ({
178
137
  label: value[0].toUpperCase() + value.slice(1),
179
138
  value,
180
139
  })), value: settings.logLevel }), (error !== null && error !== void 0 ? error : settingsError) ? (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [