@remotion/studio
Version:
APIs for interacting with the Remotion Studio
910 lines (909 loc) • 39.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeSearchResults = exports.useMenuStructure = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const remotion_1 = require("remotion");
const no_react_1 = require("remotion/no-react");
const restart_studio_1 = require("../api/restart-studio");
const AskAiModal_1 = require("../components/AskAiModal");
const layout_1 = require("../components/layout");
const NotificationCenter_1 = require("../components/Notifications/NotificationCenter");
const SizeSelector_1 = require("../components/SizeSelector");
const TimelineInOutToggle_1 = require("../components/TimelineInOutToggle");
const ShortcutHint_1 = require("../error-overlay/remotion-overlay/ShortcutHint");
const Checkmark_1 = require("../icons/Checkmark");
const canvas_ref_1 = require("../state/canvas-ref");
const checkerboard_1 = require("../state/checkerboard");
const editor_guides_1 = require("../state/editor-guides");
const editor_rulers_1 = require("../state/editor-rulers");
const editor_zoom_gestures_1 = require("../state/editor-zoom-gestures");
const modals_1 = require("../state/modals");
const sidebar_1 = require("../state/sidebar");
const check_fullscreen_support_1 = require("./check-fullscreen-support");
const client_id_1 = require("./client-id");
const get_git_menu_item_1 = require("./get-git-menu-item");
const mobile_layout_1 = require("./mobile-layout");
const open_in_editor_1 = require("./open-in-editor");
const pick_color_1 = require("./pick-color");
const show_browser_rendering_1 = require("./show-browser-rendering");
const use_keybinding_1 = require("./use-keybinding");
const openExternal = (link) => {
window.open(link, '_blank');
};
const rotate = {
transform: `rotate(90deg)`,
};
const ICON_SIZE = 16;
const getFileMenu = ({ readOnlyStudio, closeMenu, previewServerState, setSelectedModal, }) => {
const items = [
window.remotion_isReadOnlyStudio
? {
id: 'input-props-override',
value: 'input-props-override',
label: 'Set input props...',
onClick: () => {
closeMenu();
setSelectedModal({
type: 'input-props-override',
});
},
type: 'item',
keyHint: null,
leftItem: null,
subMenu: null,
quickSwitcherLabel: 'Override input props',
}
: null,
readOnlyStudio
? null
: {
id: 'render',
value: 'render',
label: 'Render...',
onClick: () => {
closeMenu();
if (previewServerState !== 'connected') {
(0, NotificationCenter_1.showNotification)('Restart the studio to render', 2000);
return;
}
const renderButton = document.getElementById('render-modal-button-server');
renderButton.click();
},
type: 'item',
keyHint: 'R',
leftItem: null,
subMenu: null,
quickSwitcherLabel: 'Render...',
},
show_browser_rendering_1.SHOW_BROWSER_RENDERING && !readOnlyStudio
? {
id: 'render-on-web',
value: 'render-on-web',
label: 'Render on web...',
onClick: () => {
closeMenu();
const renderButton = document.getElementById('render-modal-button-client');
renderButton.click();
},
type: 'item',
keyHint: null,
leftItem: null,
subMenu: null,
quickSwitcherLabel: 'Render on web...',
}
: null,
window.remotion_editorName && !readOnlyStudio
? {
type: 'divider',
id: 'open-in-editor-divider',
}
: null,
window.remotion_editorName && !readOnlyStudio
? {
id: 'open-in-editor',
value: 'open-in-editor',
label: `Open in ${window.remotion_editorName}`,
onClick: async () => {
await (0, open_in_editor_1.openInEditor)({
originalFileName: `${window.remotion_cwd}`,
originalLineNumber: 1,
originalColumnNumber: 1,
originalFunctionName: null,
originalScriptCode: null,
})
.then((res) => res.json())
.then(({ success }) => {
if (!success) {
(0, NotificationCenter_1.showNotification)(`Could not open ${window.remotion_editorName}`, 2000);
}
})
.catch((err) => {
// eslint-disable-next-line no-console
console.error(err);
(0, NotificationCenter_1.showNotification)(`Could not open ${window.remotion_editorName}`, 2000);
});
},
type: 'item',
keyHint: null,
leftItem: null,
subMenu: null,
quickSwitcherLabel: 'Open in editor...',
}
: null,
(0, get_git_menu_item_1.getGitMenuItem)(),
].filter(no_react_1.NoReactInternals.truthy);
if (items.length === 0) {
return null;
}
return {
id: 'file',
label: 'File',
leaveLeftPadding: false,
items,
quickSwitcherLabel: null,
};
};
const useMenuStructure = (closeMenu, readOnlyStudio) => {
const { setSelectedModal } = (0, react_1.useContext)(modals_1.ModalsContext);
const { checkerboard, setCheckerboard } = (0, react_1.useContext)(checkerboard_1.CheckerboardContext);
const { editorZoomGestures, setEditorZoomGestures } = (0, react_1.useContext)(editor_zoom_gestures_1.EditorZoomGesturesContext);
const { editorShowRulers, setEditorShowRulers } = (0, react_1.useContext)(editor_rulers_1.EditorShowRulersContext);
const { editorShowGuides, setEditorShowGuides } = (0, react_1.useContext)(editor_guides_1.EditorShowGuidesContext);
const { size, setSize } = (0, react_1.useContext)(remotion_1.Internals.PreviewSizeContext);
const { type } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx).previewServerState;
const { setSidebarCollapsedState, sidebarCollapsedStateLeft, sidebarCollapsedStateRight, } = (0, react_1.useContext)(sidebar_1.SidebarContext);
const sizes = (0, react_1.useMemo)(() => (0, SizeSelector_1.getUniqueSizes)(size), [size]);
const isFullscreenSupported = (0, check_fullscreen_support_1.checkFullscreenSupport)();
const { remotion_packageManager } = window;
const sizePreselectIndex = sizes.findIndex((s) => String(size.size) === String(s.size));
const mobileLayout = (0, mobile_layout_1.useMobileLayout)();
const structure = (0, react_1.useMemo)(() => {
let struct = [
{
id: 'remotion',
label: (jsx_runtime_1.jsx(layout_1.Row, { align: "center", justify: "center", children: jsx_runtime_1.jsx("svg", { width: ICON_SIZE, height: ICON_SIZE, viewBox: "-100 -100 400 400", style: rotate, children: jsx_runtime_1.jsx("path", { fill: "#fff", stroke: "#fff", strokeWidth: "100", strokeLinejoin: "round", d: "M 2 172 a 196 100 0 0 0 195 5 A 196 240 0 0 0 100 2.259 A 196 240 0 0 0 2 172 z" }) }) })),
leaveLeftPadding: false,
items: [
{
id: 'about',
value: 'about',
label: 'About Remotion',
onClick: () => {
closeMenu();
openExternal('https://remotion.dev');
},
type: 'item',
keyHint: null,
leftItem: null,
subMenu: null,
quickSwitcherLabel: 'Help: About Remotion',
},
{
id: 'changelog',
value: 'changelog',
label: 'Changelog',
onClick: () => {
closeMenu();
openExternal('https://github.com/remotion-dev/remotion/releases');
},
type: 'item',
keyHint: null,
leftItem: null,
subMenu: null,
quickSwitcherLabel: 'Help: Changelog',
},
{
id: 'license',
value: 'license',
label: 'License',
onClick: () => {
closeMenu();
openExternal('https://github.com/remotion-dev/remotion/blob/main/LICENSE.md');
},
type: 'item',
keyHint: null,
leftItem: null,
subMenu: null,
quickSwitcherLabel: 'Help: License',
},
{
id: 'acknowledgements',
value: 'acknowledgements',
label: 'Acknowledgements',
onClick: () => {
closeMenu();
openExternal('https://remotion.dev/acknowledgements');
},
type: 'item',
keyHint: null,
leftItem: null,
subMenu: null,
quickSwitcherLabel: 'Help: Acknowledgements',
},
{
type: 'divider',
id: 'timeline-divider-1',
},
{
id: 'restart-studio',
value: 'restart-studio',
label: 'Restart Studio Server',
onClick: () => {
closeMenu();
(0, restart_studio_1.restartStudio)();
},
type: 'item',
keyHint: null,
leftItem: null,
subMenu: null,
quickSwitcherLabel: 'Restart Studio Server',
},
],
quickSwitcherLabel: null,
},
getFileMenu({
readOnlyStudio,
closeMenu,
previewServerState: type,
setSelectedModal,
}),
{
id: 'view',
label: 'View',
leaveLeftPadding: true,
items: [
{
id: 'preview-size',
keyHint: null,
label: 'Preview size',
onClick: () => undefined,
type: 'item',
value: 'preview-size',
leftItem: null,
subMenu: {
leaveLeftSpace: true,
preselectIndex: sizePreselectIndex,
items: sizes.map((newSize) => ({
id: String(newSize.size),
keyHint: newSize.size === 1 ? '0' : null,
label: (0, SizeSelector_1.getPreviewSizeLabel)(newSize),
leftItem: String(newSize.size) === String(size.size) ? (jsx_runtime_1.jsx(Checkmark_1.Checkmark, {})) : null,
onClick: () => {
closeMenu();
setSize(() => newSize);
},
subMenu: null,
type: 'item',
value: newSize.size,
quickSwitcherLabel: null,
})),
quickSwitcherLabel: null,
},
quickSwitcherLabel: null,
},
{
id: 'editor-zoom-gestures',
keyHint: null,
label: 'Zoom and Pan Gestures',
onClick: () => {
closeMenu();
setEditorZoomGestures((c) => !c);
},
type: 'item',
value: 'editor-zoom-gestures',
leftItem: editorZoomGestures ? jsx_runtime_1.jsx(Checkmark_1.Checkmark, {}) : null,
subMenu: null,
quickSwitcherLabel: editorZoomGestures
? 'Disable Zoom and Pan Gestures'
: 'Enable Zoom and Pan Gestures',
},
{
id: 'show-rulers',
keyHint: null,
label: 'Show Rulers',
onClick: () => {
closeMenu();
setEditorShowRulers((c) => !c);
},
type: 'item',
value: 'show-ruler',
leftItem: editorShowRulers ? jsx_runtime_1.jsx(Checkmark_1.Checkmark, {}) : null,
subMenu: null,
quickSwitcherLabel: editorShowRulers
? 'Hide Rulers'
: 'Show Rulers',
},
{
id: 'show-guides',
keyHint: null,
label: 'Show Guides',
onClick: () => {
closeMenu();
setEditorShowGuides((c) => !c);
},
type: 'item',
value: 'show-guides',
leftItem: editorShowGuides ? jsx_runtime_1.jsx(Checkmark_1.Checkmark, {}) : null,
subMenu: null,
quickSwitcherLabel: editorShowGuides
? 'Hide Guides'
: 'Show Guides',
},
{
id: 'timeline-divider-1',
type: 'divider',
},
{
id: 'left-sidebar',
label: 'Left Sidebar',
keyHint: null,
type: 'item',
value: 'preview-size',
leftItem: null,
quickSwitcherLabel: null,
subMenu: {
leaveLeftSpace: true,
preselectIndex: 0,
items: [
{
id: 'left-sidebar-responsive',
keyHint: null,
label: 'Responsive',
leftItem: sidebarCollapsedStateLeft === 'responsive' ? (jsx_runtime_1.jsx(Checkmark_1.Checkmark, {})) : null,
onClick: () => {
closeMenu();
setSidebarCollapsedState({
left: 'responsive',
right: null,
});
},
subMenu: null,
type: 'item',
value: 'responsive',
quickSwitcherLabel: null,
},
{
id: 'left-sidebar-expanded',
keyHint: null,
label: 'Expanded',
leftItem: sidebarCollapsedStateLeft === 'expanded' ? (jsx_runtime_1.jsx(Checkmark_1.Checkmark, {})) : null,
onClick: () => {
closeMenu();
setSidebarCollapsedState({ left: 'expanded', right: null });
},
subMenu: null,
type: 'item',
value: 'expanded',
quickSwitcherLabel: 'Expand',
},
{
id: 'left-sidebar-collapsed',
keyHint: null,
label: 'Collapsed',
leftItem: sidebarCollapsedStateLeft === 'collapsed' ? (jsx_runtime_1.jsx(Checkmark_1.Checkmark, {})) : null,
onClick: () => {
closeMenu();
setSidebarCollapsedState({
left: 'collapsed',
right: null,
});
},
subMenu: null,
type: 'item',
value: 'collapsed',
quickSwitcherLabel: 'Collapse',
},
],
},
onClick: () => undefined,
},
{
id: 'right-sidebar',
label: 'Right Sidebar',
keyHint: null,
type: 'item',
value: 'preview-size',
leftItem: null,
quickSwitcherLabel: null,
subMenu: {
leaveLeftSpace: true,
preselectIndex: 0,
items: [
{
id: 'sidebar-expanded',
keyHint: null,
label: 'Expanded',
leftItem: sidebarCollapsedStateRight === 'expanded' ? (jsx_runtime_1.jsx(Checkmark_1.Checkmark, {})) : null,
onClick: () => {
closeMenu();
setSidebarCollapsedState({ left: null, right: 'expanded' });
},
subMenu: null,
type: 'item',
value: 'expanded',
quickSwitcherLabel: 'Expand',
},
{
id: 'right-sidebar-collapsed',
keyHint: null,
label: 'Collapsed',
leftItem: sidebarCollapsedStateRight === 'collapsed' ? (jsx_runtime_1.jsx(Checkmark_1.Checkmark, {})) : null,
onClick: () => {
closeMenu();
setSidebarCollapsedState({
left: null,
right: 'collapsed',
});
},
subMenu: null,
type: 'item',
value: 'collapsed',
quickSwitcherLabel: 'Collapse',
},
],
},
onClick: () => undefined,
},
{
id: 'timeline-divider-2',
type: 'divider',
},
{
id: 'checkerboard',
keyHint: 'T',
label: 'Transparency as checkerboard',
onClick: () => {
closeMenu();
setCheckerboard((c) => !c);
},
type: 'item',
value: 'checkerboard',
leftItem: checkerboard ? jsx_runtime_1.jsx(Checkmark_1.Checkmark, {}) : null,
subMenu: null,
quickSwitcherLabel: checkerboard
? 'Disable Checkerboard Transparency'
: 'Enable Checkerboard Transparency',
},
{
id: 'timeline-divider-3',
type: 'divider',
},
{
id: 'quick-switcher',
keyHint: `${ShortcutHint_1.cmdOrCtrlCharacter}+K`,
label: 'Quick Switcher',
onClick: () => {
closeMenu();
setSelectedModal({
type: 'quick-switcher',
mode: 'compositions',
invocationTimestamp: Date.now(),
});
},
type: 'item',
value: 'quick-switcher',
leftItem: null,
subMenu: null,
quickSwitcherLabel: 'Switch composition',
},
{
id: 'in-out-divider-5',
type: 'divider',
},
{
id: 'in-mark',
keyHint: 'I',
label: 'In Mark',
leftItem: null,
onClick: () => {
var _a;
closeMenu();
(_a = TimelineInOutToggle_1.inOutHandles.current) === null || _a === void 0 ? void 0 : _a.inMarkClick(null);
},
subMenu: null,
type: 'item',
value: 'in-mark',
quickSwitcherLabel: 'Timeline: Set In Mark',
},
{
id: 'out-mark',
keyHint: 'O',
label: 'Out Mark',
leftItem: null,
onClick: () => {
var _a;
closeMenu();
(_a = TimelineInOutToggle_1.inOutHandles.current) === null || _a === void 0 ? void 0 : _a.outMarkClick(null);
},
subMenu: null,
type: 'item',
value: 'out-mark',
quickSwitcherLabel: 'Timeline: Set Out Mark',
},
{
id: 'x-mark',
keyHint: 'X',
label: 'Clear In/Out Marks',
leftItem: null,
onClick: () => {
var _a;
closeMenu();
(_a = TimelineInOutToggle_1.inOutHandles.current) === null || _a === void 0 ? void 0 : _a.clearMarks();
},
subMenu: null,
type: 'item',
value: 'clear-marks',
quickSwitcherLabel: 'Timeline: Clear In and Out Mark',
},
{
id: 'goto-time',
keyHint: 'G',
label: 'Go to frame',
leftItem: null,
onClick: () => {
var _a;
closeMenu();
(_a = remotion_1.Internals.timeValueRef.current) === null || _a === void 0 ? void 0 : _a.goToFrame();
},
subMenu: null,
type: 'item',
value: 'clear-marks',
quickSwitcherLabel: 'Timeline: Go to frame',
},
{
id: 'fullscreen-divider',
type: 'divider',
},
isFullscreenSupported
? {
id: 'fullscreen',
keyHint: null,
label: 'Fullscreen',
leftItem: null,
onClick: () => {
var _a;
closeMenu();
(_a = canvas_ref_1.drawRef.current) === null || _a === void 0 ? void 0 : _a.requestFullscreen();
},
subMenu: null,
type: 'item',
value: 'fullscreen',
quickSwitcherLabel: 'Go Fullscreen',
}
: null,
].filter(remotion_1.Internals.truthy),
},
{
id: 'tools',
label: 'Tools',
leaveLeftPadding: false,
items: [
process.env.ASK_AI_ENABLED
? {
id: 'ask-ai',
value: 'ask-ai',
label: 'Ask AI',
onClick: () => {
var _a;
closeMenu();
(_a = AskAiModal_1.askAiModalRef.current) === null || _a === void 0 ? void 0 : _a.toggle();
},
leftItem: null,
keyHint: `${ShortcutHint_1.cmdOrCtrlCharacter}+I`,
subMenu: null,
type: 'item',
quickSwitcherLabel: 'Ask AI',
}
: null,
'EyeDropper' in window
? {
id: 'color-picker',
value: 'color-picker',
label: 'Color Picker',
onClick: () => {
closeMenu();
(0, pick_color_1.pickColor)();
},
leftItem: null,
keyHint: null,
subMenu: null,
type: 'item',
quickSwitcherLabel: 'Show Color Picker',
}
: null,
{
id: 'spring-editor',
value: 'spring-editor',
label: 'Timing Editor',
onClick: () => {
closeMenu();
window.open('https://www.remotion.dev/timing-editor', '_blank');
},
leftItem: null,
keyHint: null,
subMenu: null,
type: 'item',
quickSwitcherLabel: 'Open spring() Editor',
},
].filter(remotion_1.Internals.truthy),
quickSwitcherLabel: null,
},
readOnlyStudio || remotion_packageManager === 'unknown'
? null
: {
id: 'install',
label: 'Packages',
leaveLeftPadding: false,
items: [
{
id: 'install-packages',
value: 'install-packages',
label: 'Install...',
onClick: () => {
closeMenu();
setSelectedModal({
type: 'install-packages',
packageManager: remotion_packageManager,
});
},
type: 'item',
keyHint: null,
leftItem: null,
subMenu: null,
quickSwitcherLabel: `Install packages`,
},
],
},
{
id: 'help',
label: 'Help',
leaveLeftPadding: false,
items: [
{
id: 'shortcuts',
value: 'shortcuts',
label: (0, use_keybinding_1.areKeyboardShortcutsDisabled)()
? 'Shortcuts (disabled)'
: 'Shortcuts',
onClick: () => {
closeMenu();
setSelectedModal({
type: 'quick-switcher',
mode: 'docs',
invocationTimestamp: Date.now(),
});
},
keyHint: '?',
leftItem: null,
subMenu: null,
type: 'item',
quickSwitcherLabel: (0, use_keybinding_1.areKeyboardShortcutsDisabled)()
? 'Show all Keyboard Shortcuts (disabled)'
: 'Show all Keyboard Shortcuts',
},
{
id: 'docs',
value: 'docs',
label: 'Docs',
onClick: () => {
closeMenu();
openExternal('https://remotion.dev/docs');
},
type: 'item',
keyHint: null,
leftItem: null,
subMenu: null,
quickSwitcherLabel: 'Visit Documentation',
},
{
id: 'file-issue',
value: 'file-issue',
label: 'File an issue',
onClick: () => {
closeMenu();
openExternal('https://github.com/remotion-dev/remotion/issues/new/choose');
},
type: 'item',
keyHint: null,
leftItem: null,
subMenu: null,
quickSwitcherLabel: 'File GitHub issue',
},
{
id: 'discord',
value: 'discord',
label: 'Join Discord community',
onClick: () => {
closeMenu();
openExternal('https://discord.com/invite/6VzzNDwUwV');
},
type: 'item',
keyHint: null,
leftItem: null,
subMenu: null,
quickSwitcherLabel: null,
},
{
id: 'help-divider-6',
type: 'divider',
},
{
id: 'insta',
value: 'insta',
label: 'Instagram',
onClick: () => {
closeMenu();
openExternal('https://instagram.com/remotion');
},
type: 'item',
keyHint: null,
leftItem: null,
subMenu: null,
quickSwitcherLabel: 'Follow Remotion on Instagram',
},
{
id: 'x',
value: 'x',
label: 'X',
onClick: () => {
closeMenu();
openExternal('https://x.com/remotion');
},
type: 'item',
keyHint: null,
leftItem: null,
subMenu: null,
quickSwitcherLabel: 'Follow Remotion on X',
},
{
id: 'youtube',
value: 'youtube',
label: 'YouTube',
onClick: () => {
closeMenu();
openExternal('https://www.youtube.com/@remotion_dev');
},
type: 'item',
keyHint: null,
leftItem: null,
subMenu: null,
quickSwitcherLabel: 'Watch Remotion on YouTube',
},
{
id: 'linkedin',
value: 'linkedin',
label: 'LinkedIn',
onClick: () => {
closeMenu();
openExternal('https://www.linkedin.com/company/remotion-dev/');
},
type: 'item',
keyHint: null,
leftItem: null,
subMenu: null,
quickSwitcherLabel: 'Follow Remotion on LinkedIn',
},
{
id: 'tiktok',
value: 'tiktok',
label: 'TikTok',
onClick: () => {
closeMenu();
openExternal('https://www.tiktok.com/@remotion');
},
type: 'item',
keyHint: null,
leftItem: null,
subMenu: null,
quickSwitcherLabel: 'Follow Remotion on TikTok',
},
],
},
].filter(remotion_1.Internals.truthy);
if (mobileLayout) {
struct = [
{
...struct[0],
items: [
...struct.slice(1).map((s) => {
return {
...s,
keyHint: null,
onClick: () => undefined,
type: 'item',
value: s.id,
leftItem: null,
subMenu: {
items: s.items,
leaveLeftSpace: true,
preselectIndex: 0,
},
quickSwitcherLabel: null,
};
}),
...struct[0].items,
],
},
];
}
return struct;
}, [
readOnlyStudio,
closeMenu,
type,
sizePreselectIndex,
sizes,
editorZoomGestures,
editorShowRulers,
editorShowGuides,
sidebarCollapsedStateLeft,
sidebarCollapsedStateRight,
checkerboard,
isFullscreenSupported,
remotion_packageManager,
mobileLayout,
size.size,
setSize,
setEditorZoomGestures,
setEditorShowRulers,
setEditorShowGuides,
setSidebarCollapsedState,
setCheckerboard,
setSelectedModal,
]);
return structure;
};
exports.useMenuStructure = useMenuStructure;
const getItemLabel = (item) => {
var _a;
if (item.quickSwitcherLabel !== null) {
return item.quickSwitcherLabel;
}
if (typeof item.label === 'string') {
return item.label;
}
return (_a = item.label) === null || _a === void 0 ? void 0 : _a.toString();
};
const itemToSearchResult = (item, setSelectedModal, prefixes) => {
if (item.subMenu) {
return item.subMenu.items
.map((subItem) => {
if (subItem.type === 'divider') {
return null;
}
return itemToSearchResult(subItem, setSelectedModal, [
...prefixes,
getItemLabel(item),
]);
})
.flat(1)
.filter(no_react_1.NoReactInternals.truthy);
}
return [
{
type: 'menu-item',
id: item.id,
onSelected: () => {
setSelectedModal(null);
item.onClick(item.id, null);
},
title: [...prefixes, getItemLabel(item)].join(': '),
},
];
};
const makeSearchResults = (actions, setSelectedModal) => {
const items = actions
.map((menu) => {
return menu.items.map((item) => {
if (item.type === 'divider') {
return null;
}
return itemToSearchResult(item, setSelectedModal, []);
});
})
.flat(Infinity)
.filter(no_react_1.NoReactInternals.truthy);
return items;
};
exports.makeSearchResults = makeSearchResults;