From ef59dbda848ea61fbbf1d0656a87ffbef14125ad Mon Sep 17 00:00:00 2001 From: Mikael Finstad Date: Sun, 17 Mar 2024 23:04:11 +0800 Subject: [PATCH] add keyboard actions - Open previous file `batchOpenPreviousFile` - Open next `filebatchOpenNextFile` closes #1926 --- public/configStore.js | 2 + src/App.tsx | 37 +- src/components/KeyboardShortcuts.tsx | 932 ++++++++++++++------------- types.ts | 2 +- 4 files changed, 499 insertions(+), 474 deletions(-) diff --git a/public/configStore.js b/public/configStore.js index 4c1dcf2d..6e77d67a 100644 --- a/public/configStore.js +++ b/public/configStore.js @@ -51,12 +51,14 @@ const defaultKeyBindings = [ { keys: 'ctrl+up', action: 'timelineZoomIn' }, { keys: 'command+up', action: 'timelineZoomIn' }, { keys: 'shift+up', action: 'batchPreviousFile' }, + { keys: 'ctrl+shift+up', action: 'batchOpenPreviousFile' }, { keys: 'pagedown', action: 'jumpLastSegment' }, { keys: 'down', action: 'jumpNextSegment' }, { keys: 'ctrl+down', action: 'timelineZoomOut' }, { keys: 'command+down', action: 'timelineZoomOut' }, { keys: 'shift+down', action: 'batchNextFile' }, + { keys: 'ctrl+shift+down', action: 'batchOpenNextFile' }, { keys: 'shift+enter', action: 'batchOpenSelectedFile' }, diff --git a/src/App.tsx b/src/App.tsx index f2dd8308..4615b4f3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1653,19 +1653,24 @@ function App() { } }, [userOpenSingleFile, setWorking, filePath]); - const batchFileJump = useCallback((direction) => { + const batchFileJump = useCallback((direction: number, alsoOpen: boolean) => { if (batchFiles.length === 0) return; + + let newSelectedBatchFiles: string[]; if (selectedBatchFiles.length === 0) { - setSelectedBatchFiles([batchFiles[0]!.path]); - return; + newSelectedBatchFiles = [batchFiles[0]!.path]; + } else { + const selectedFilePath = selectedBatchFiles[direction > 0 ? selectedBatchFiles.length - 1 : 0]; + const pathIndex = batchFiles.findIndex(({ path }) => path === selectedFilePath); + if (pathIndex === -1) return; + const nextFile = batchFiles[pathIndex + direction]; + if (!nextFile) return; + newSelectedBatchFiles = [nextFile.path]; } - const selectedFilePath = selectedBatchFiles[direction > 0 ? selectedBatchFiles.length - 1 : 0]; - const pathIndex = batchFiles.findIndex(({ path }) => path === selectedFilePath); - if (pathIndex === -1) return; - const nextFile = batchFiles[pathIndex + direction]; - if (!nextFile) return; - setSelectedBatchFiles([nextFile.path]); - }, [batchFiles, selectedBatchFiles]); + + setSelectedBatchFiles(newSelectedBatchFiles); + if (alsoOpen) batchOpenSingleFile(newSelectedBatchFiles[0]); + }, [batchFiles, batchOpenSingleFile, selectedBatchFiles]); const batchOpenSelectedFile = useCallback(() => { if (selectedBatchFiles.length === 0) return; @@ -2032,7 +2037,7 @@ function App() { type MainKeyboardAction = Exclude; - const mainActions = useMemo boolean) | ((a: { keyup?: boolean | undefined }) => void)>>(() => { + const mainActions = useMemo(() => { async function exportYouTube() { if (!checkFileOpened()) return; @@ -2043,7 +2048,7 @@ function App() { seekAccelerationRef.current = 1; } - return { + const ret: Record boolean) | ((a: { keyup?: boolean | undefined }) => void)> = { // NOTE: Do not change these keys because users have bound keys by these names in their config files // For actions, see also KeyboardShortcuts.jsx togglePlayNoResetSpeed: () => togglePlay(), @@ -2099,8 +2104,10 @@ function App() { jumpTimelineEnd, timelineZoomIn: () => { zoomRel(1); return false; }, timelineZoomOut: () => { zoomRel(-1); return false; }, - batchPreviousFile: () => batchFileJump(-1), - batchNextFile: () => batchFileJump(1), + batchPreviousFile: () => batchFileJump(-1, false), + batchNextFile: () => batchFileJump(1, false), + batchOpenPreviousFile: () => batchFileJump(-1, true), + batchOpenNextFile: () => batchFileJump(1, true), batchOpenSelectedFile, closeBatch, removeCurrentSegment: () => removeCutSegment(currentSegIndexSafe), @@ -2172,6 +2179,8 @@ function App() { showIncludeExternalStreamsDialog, toggleFullscreenVideo, }; + + return ret; }, [addSegment, alignSegmentTimesToKeyframes, apparentCutSegments, askStartTimeOffset, batchFileJump, batchOpenSelectedFile, captureSnapshot, captureSnapshotAsCoverArt, changePlaybackRate, checkFileOpened, cleanupFilesDialog, clearSegments, closeBatch, closeFileWithConfirm, combineOverlappingSegments, combineSelectedSegments, concatBatch, convertFormatBatch, copySegmentsToClipboard, createFixedDurationSegments, createNumSegments, createRandomSegments, createSegmentsFromKeyframes, currentSegIndexSafe, cutSegments.length, cutSegmentsHistory, deselectAllSegments, detectBlackScenes, detectSceneChanges, detectSilentScenes, duplicateCurrentSegment, editCurrentSegmentTags, extractAllStreams, extractCurrentSegmentFramesAsImages, extractSelectedSegmentsFramesAsImages, fillSegmentsGaps, goToTimecode, handleShowStreamsSelectorClick, increaseRotation, invertAllSegments, invertSelectedSegments, jumpCutEnd, jumpCutStart, jumpSeg, jumpTimelineEnd, jumpTimelineStart, keyboardNormalSeekSpeed, keyboardSeekAccFactor, onExportPress, onLabelSegment, openFilesDialog, openSendReportDialogWithState, pause, play, removeCutSegment, removeSelectedSegments, reorderSegsByStartTime, seekClosestKeyframe, seekRel, seekRelPercent, selectAllSegments, selectOnlyCurrentSegment, setCurrentSegIndex, setCutEnd, setCutStart, setPlaybackVolume, shiftAllSegmentTimes, shortStep, showIncludeExternalStreamsDialog, shuffleSegments, splitCurrentSegment, timelineToggleComfortZoom, toggleCaptureFormat, toggleCurrentSegmentSelected, toggleFullscreenVideo, toggleKeyframeCut, toggleLastCommands, toggleLoopSelectedSegments, togglePlay, toggleSegmentsList, toggleSettings, toggleShowKeyframes, toggleShowThumbnails, toggleStreamsSelector, toggleStripAudio, toggleStripThumbnail, toggleWaveformMode, tryFixInvalidDuration, userHtml5ifyCurrentFile, zoomRel]); const getKeyboardAction = useCallback((action: MainKeyboardAction) => mainActions[action], [mainActions]); diff --git a/src/components/KeyboardShortcuts.tsx b/src/components/KeyboardShortcuts.tsx index 2f445129..34a65f24 100644 --- a/src/components/KeyboardShortcuts.tsx +++ b/src/components/KeyboardShortcuts.tsx @@ -126,7 +126,7 @@ const KeyboardShortcuts = memo(({ const { mouseWheelZoomModifierKey } = useUserSettings(); - const { actionsMap, extraLinesPerCategory } = useMemo<{ actionsMap: ActionsMap, extraLinesPerCategory: Record }>(() => { + const { actionsMap, extraLinesPerCategory } = useMemo(() => { const playbackCategory = t('Playback'); const selectivePlaybackCategory = t('Playback/preview segments only'); const seekingCategory = t('Seeking'); @@ -137,474 +137,488 @@ const KeyboardShortcuts = memo(({ const otherCategory = t('Other operations'); const streamsCategory = t('Tracks'); - return { - extraLinesPerCategory: { - [zoomOperationsCategory]: [ -
- {t('Zoom in/out timeline')} -
- - {t('Mouse scroll/wheel up/down')} -
, - -
- {t('Pan timeline')} -
- {getModifier(mouseWheelZoomModifierKey).map((v) => {v})} - - {t('Mouse scroll/wheel up/down')} -
, - ], + // eslint-disable-next-line no-shadow + const actionsMap: ActionsMap = { + toggleLastCommands: { + name: t('Last ffmpeg commands'), + }, + toggleKeyboardShortcuts: { + name: t('Keyboard & mouse shortcuts'), }, - actionsMap: { - toggleLastCommands: { - name: t('Last ffmpeg commands'), - }, - toggleKeyboardShortcuts: { - name: t('Keyboard & mouse shortcuts'), - }, - // playbackCategory - togglePlayResetSpeed: { - name: t('Play/pause'), - category: playbackCategory, - }, - togglePlayNoResetSpeed: { - name: t('Play/pause (no reset speed)'), - category: playbackCategory, - }, - play: { - name: t('Play'), - category: playbackCategory, - }, - pause: { - name: t('Pause'), - category: playbackCategory, - }, - increasePlaybackRate: { - name: t('Speed up playback'), - category: playbackCategory, - }, - reducePlaybackRate: { - name: t('Slow down playback'), - category: playbackCategory, - }, - increasePlaybackRateMore: { - name: t('Speed up playback more'), - category: playbackCategory, - }, - reducePlaybackRateMore: { - name: t('Slow down playback more'), - category: playbackCategory, - }, - increaseVolume: { - name: t('Increase audio volume'), - category: playbackCategory, - }, - decreaseVolume: { - name: t('Decrease audio volume'), - category: playbackCategory, - }, - reloadFile: { - name: t('Reload current media'), - category: playbackCategory, - }, - html5ify: { - name: t('Convert to supported format'), - category: playbackCategory, - }, + // playbackCategory + togglePlayResetSpeed: { + name: t('Play/pause'), + category: playbackCategory, + }, + togglePlayNoResetSpeed: { + name: t('Play/pause (no reset speed)'), + category: playbackCategory, + }, + play: { + name: t('Play'), + category: playbackCategory, + }, + pause: { + name: t('Pause'), + category: playbackCategory, + }, + increasePlaybackRate: { + name: t('Speed up playback'), + category: playbackCategory, + }, + reducePlaybackRate: { + name: t('Slow down playback'), + category: playbackCategory, + }, + increasePlaybackRateMore: { + name: t('Speed up playback more'), + category: playbackCategory, + }, + reducePlaybackRateMore: { + name: t('Slow down playback more'), + category: playbackCategory, + }, + increaseVolume: { + name: t('Increase audio volume'), + category: playbackCategory, + }, + decreaseVolume: { + name: t('Decrease audio volume'), + category: playbackCategory, + }, + reloadFile: { + name: t('Reload current media'), + category: playbackCategory, + }, + html5ify: { + name: t('Convert to supported format'), + category: playbackCategory, + }, - // selectivePlaybackCategory - togglePlayOnlyCurrentSegment: { - name: t('Play current segment once'), - category: selectivePlaybackCategory, - }, - toggleLoopOnlyCurrentSegment: { - name: t('Loop current segment'), - category: selectivePlaybackCategory, - }, - toggleLoopStartEndOnlyCurrentSegment: { - name: t('Loop beginning and end of current segment'), - category: selectivePlaybackCategory, - }, - toggleLoopSelectedSegments: { - name: t('Play selected segments in order'), - category: selectivePlaybackCategory, - }, + // selectivePlaybackCategory + togglePlayOnlyCurrentSegment: { + name: t('Play current segment once'), + category: selectivePlaybackCategory, + }, + toggleLoopOnlyCurrentSegment: { + name: t('Loop current segment'), + category: selectivePlaybackCategory, + }, + toggleLoopStartEndOnlyCurrentSegment: { + name: t('Loop beginning and end of current segment'), + category: selectivePlaybackCategory, + }, + toggleLoopSelectedSegments: { + name: t('Play selected segments in order'), + category: selectivePlaybackCategory, + }, - // seekingCategory - seekPreviousFrame: { - name: t('Step backward 1 frame'), - category: seekingCategory, - }, - seekNextFrame: { - name: t('Step forward 1 frame'), - category: seekingCategory, - }, - seekBackwards: { - name: t('Seek backward 1 sec'), - category: seekingCategory, - }, - seekForwards: { - name: t('Seek forward 1 sec'), - category: seekingCategory, - }, - seekBackwardsKeyframe: { - name: t('Seek previous keyframe'), - category: seekingCategory, - }, - seekForwardsKeyframe: { - name: t('Seek next keyframe'), - category: seekingCategory, - }, - seekBackwardsPercent: { - name: t('Seek backward 1% of timeline at current zoom'), - category: seekingCategory, - }, - seekForwardsPercent: { - name: t('Seek forward 1% of timeline at current zoom'), - category: seekingCategory, - }, - jumpCutStart: { - name: t('Jump to current segment\'s start time'), - category: seekingCategory, - before: , - }, - jumpCutEnd: { - name: t('Jump to current segment\'s end time'), - category: seekingCategory, - before: , - }, - jumpTimelineStart: { - name: t('Jump to start of video'), - category: seekingCategory, - }, - jumpTimelineEnd: { - name: t('Jump to end of video'), - category: seekingCategory, - }, - goToTimecode: { - name: t('Seek to timecode'), - category: seekingCategory, - }, + // seekingCategory + seekPreviousFrame: { + name: t('Step backward 1 frame'), + category: seekingCategory, + }, + seekNextFrame: { + name: t('Step forward 1 frame'), + category: seekingCategory, + }, + seekBackwards: { + name: t('Seek backward 1 sec'), + category: seekingCategory, + }, + seekForwards: { + name: t('Seek forward 1 sec'), + category: seekingCategory, + }, + seekBackwardsKeyframe: { + name: t('Seek previous keyframe'), + category: seekingCategory, + }, + seekForwardsKeyframe: { + name: t('Seek next keyframe'), + category: seekingCategory, + }, + seekBackwardsPercent: { + name: t('Seek backward 1% of timeline at current zoom'), + category: seekingCategory, + }, + seekForwardsPercent: { + name: t('Seek forward 1% of timeline at current zoom'), + category: seekingCategory, + }, + jumpCutStart: { + name: t('Jump to current segment\'s start time'), + category: seekingCategory, + before: , + }, + jumpCutEnd: { + name: t('Jump to current segment\'s end time'), + category: seekingCategory, + before: , + }, + jumpTimelineStart: { + name: t('Jump to start of video'), + category: seekingCategory, + }, + jumpTimelineEnd: { + name: t('Jump to end of video'), + category: seekingCategory, + }, + goToTimecode: { + name: t('Seek to timecode'), + category: seekingCategory, + }, - // segmentsAndCutpointsCategory - addSegment: { - name: t('Add cut segment'), - category: segmentsAndCutpointsCategory, - }, - removeCurrentSegment: { - name: t('Remove current segment'), - category: segmentsAndCutpointsCategory, - }, - setCutStart: { - name: t('Start current segment at current time'), - category: segmentsAndCutpointsCategory, - before: , - }, - setCutEnd: { - name: t('End current segment at current time'), - category: segmentsAndCutpointsCategory, - before: , - }, - labelCurrentSegment: { - name: t('Label current segment'), - category: segmentsAndCutpointsCategory, - }, - editCurrentSegmentTags: { - name: t('Edit current segment tags'), - category: segmentsAndCutpointsCategory, - }, - splitCurrentSegment: { - name: t('Split segment at cursor'), - category: segmentsAndCutpointsCategory, - }, - duplicateCurrentSegment: { - name: t('Duplicate current segment'), - category: segmentsAndCutpointsCategory, - }, - jumpPrevSegment: { - name: t('Jump to previous segment'), - category: segmentsAndCutpointsCategory, - }, - jumpNextSegment: { - name: t('Jump to next segment'), - category: segmentsAndCutpointsCategory, - }, - jumpFirstSegment: { - name: t('Jump to first segment'), - category: segmentsAndCutpointsCategory, - }, - jumpLastSegment: { - name: t('Jump to last segment'), - category: segmentsAndCutpointsCategory, - }, - reorderSegsByStartTime: { - name: t('Reorder segments by start time'), - category: segmentsAndCutpointsCategory, - }, - invertAllSegments: { - name: t('Invert all segments on timeline'), - category: segmentsAndCutpointsCategory, - }, - fillSegmentsGaps: { - name: t('Fill gaps between segments'), - category: segmentsAndCutpointsCategory, - }, - shiftAllSegmentTimes: { - name: t('Shift all segments on timeline'), - category: segmentsAndCutpointsCategory, - }, - alignSegmentTimesToKeyframes: { - name: t('Align segment times to keyframes'), - category: segmentsAndCutpointsCategory, - }, - createSegmentsFromKeyframes: { - name: t('Create segments from keyframes'), - category: segmentsAndCutpointsCategory, - }, - createFixedDurationSegments: { - name: t('Create fixed duration segments'), - category: segmentsAndCutpointsCategory, - }, - createNumSegments: { - name: t('Create num segments'), - category: segmentsAndCutpointsCategory, - }, - createRandomSegments: { - name: t('Create random segments'), - category: segmentsAndCutpointsCategory, - }, - detectBlackScenes: { - name: t('Detect black scenes'), - category: segmentsAndCutpointsCategory, - }, - detectSilentScenes: { - name: t('Detect silent scenes'), - category: segmentsAndCutpointsCategory, - }, - detectSceneChanges: { - name: t('Detect scene changes'), - category: segmentsAndCutpointsCategory, - }, - shuffleSegments: { - name: t('Shuffle segments order'), - category: segmentsAndCutpointsCategory, - }, - combineOverlappingSegments: { - name: t('Combine overlapping segments'), - category: segmentsAndCutpointsCategory, - }, - combineSelectedSegments: { - name: t('Combine selected segments'), - }, - clearSegments: { - name: t('Clear all segments'), - category: segmentsAndCutpointsCategory, - }, - toggleSegmentsList: { - name: t('Show sidebar'), - category: segmentsAndCutpointsCategory, - }, - selectOnlyCurrentSegment: { - name: t('Select only this segment'), - category: segmentsAndCutpointsCategory, - }, - deselectAllSegments: { - name: t('Deselect all segments'), - category: segmentsAndCutpointsCategory, - }, - selectAllSegments: { - name: t('Select all segments'), - category: segmentsAndCutpointsCategory, - }, - toggleCurrentSegmentSelected: { - name: t('Toggle current segment selected'), - category: segmentsAndCutpointsCategory, - }, - invertSelectedSegments: { - name: t('Invert selected segments'), - category: segmentsAndCutpointsCategory, - }, - removeSelectedSegments: { - name: t('Remove selected segments'), - category: segmentsAndCutpointsCategory, - }, + // segmentsAndCutpointsCategory + addSegment: { + name: t('Add cut segment'), + category: segmentsAndCutpointsCategory, + }, + removeCurrentSegment: { + name: t('Remove current segment'), + category: segmentsAndCutpointsCategory, + }, + setCutStart: { + name: t('Start current segment at current time'), + category: segmentsAndCutpointsCategory, + before: , + }, + setCutEnd: { + name: t('End current segment at current time'), + category: segmentsAndCutpointsCategory, + before: , + }, + labelCurrentSegment: { + name: t('Label current segment'), + category: segmentsAndCutpointsCategory, + }, + editCurrentSegmentTags: { + name: t('Edit current segment tags'), + category: segmentsAndCutpointsCategory, + }, + splitCurrentSegment: { + name: t('Split segment at cursor'), + category: segmentsAndCutpointsCategory, + }, + duplicateCurrentSegment: { + name: t('Duplicate current segment'), + category: segmentsAndCutpointsCategory, + }, + jumpPrevSegment: { + name: t('Jump to previous segment'), + category: segmentsAndCutpointsCategory, + }, + jumpNextSegment: { + name: t('Jump to next segment'), + category: segmentsAndCutpointsCategory, + }, + jumpFirstSegment: { + name: t('Jump to first segment'), + category: segmentsAndCutpointsCategory, + }, + jumpLastSegment: { + name: t('Jump to last segment'), + category: segmentsAndCutpointsCategory, + }, + reorderSegsByStartTime: { + name: t('Reorder segments by start time'), + category: segmentsAndCutpointsCategory, + }, + invertAllSegments: { + name: t('Invert all segments on timeline'), + category: segmentsAndCutpointsCategory, + }, + fillSegmentsGaps: { + name: t('Fill gaps between segments'), + category: segmentsAndCutpointsCategory, + }, + shiftAllSegmentTimes: { + name: t('Shift all segments on timeline'), + category: segmentsAndCutpointsCategory, + }, + alignSegmentTimesToKeyframes: { + name: t('Align segment times to keyframes'), + category: segmentsAndCutpointsCategory, + }, + createSegmentsFromKeyframes: { + name: t('Create segments from keyframes'), + category: segmentsAndCutpointsCategory, + }, + createFixedDurationSegments: { + name: t('Create fixed duration segments'), + category: segmentsAndCutpointsCategory, + }, + createNumSegments: { + name: t('Create num segments'), + category: segmentsAndCutpointsCategory, + }, + createRandomSegments: { + name: t('Create random segments'), + category: segmentsAndCutpointsCategory, + }, + detectBlackScenes: { + name: t('Detect black scenes'), + category: segmentsAndCutpointsCategory, + }, + detectSilentScenes: { + name: t('Detect silent scenes'), + category: segmentsAndCutpointsCategory, + }, + detectSceneChanges: { + name: t('Detect scene changes'), + category: segmentsAndCutpointsCategory, + }, + shuffleSegments: { + name: t('Shuffle segments order'), + category: segmentsAndCutpointsCategory, + }, + combineOverlappingSegments: { + name: t('Combine overlapping segments'), + category: segmentsAndCutpointsCategory, + }, + combineSelectedSegments: { + name: t('Combine selected segments'), + }, + clearSegments: { + name: t('Clear all segments'), + category: segmentsAndCutpointsCategory, + }, + toggleSegmentsList: { + name: t('Show sidebar'), + category: segmentsAndCutpointsCategory, + }, + selectOnlyCurrentSegment: { + name: t('Select only this segment'), + category: segmentsAndCutpointsCategory, + }, + deselectAllSegments: { + name: t('Deselect all segments'), + category: segmentsAndCutpointsCategory, + }, + selectAllSegments: { + name: t('Select all segments'), + category: segmentsAndCutpointsCategory, + }, + toggleCurrentSegmentSelected: { + name: t('Toggle current segment selected'), + category: segmentsAndCutpointsCategory, + }, + invertSelectedSegments: { + name: t('Invert selected segments'), + category: segmentsAndCutpointsCategory, + }, + removeSelectedSegments: { + name: t('Remove selected segments'), + category: segmentsAndCutpointsCategory, + }, - // streamsCategory - toggleStreamsSelector: { - name: t('Edit tracks / metadata tags'), - category: streamsCategory, - }, - extractAllStreams: { - name: t('Extract all tracks'), - category: streamsCategory, - }, - showStreamsSelector: { - name: t('Edit tracks / metadata tags'), - category: streamsCategory, - }, - showIncludeExternalStreamsDialog: { - name: t('Include more tracks from other file'), - category: streamsCategory, - }, + // streamsCategory + toggleStreamsSelector: { + name: t('Edit tracks / metadata tags'), + category: streamsCategory, + }, + extractAllStreams: { + name: t('Extract all tracks'), + category: streamsCategory, + }, + showStreamsSelector: { + name: t('Edit tracks / metadata tags'), + category: streamsCategory, + }, + showIncludeExternalStreamsDialog: { + name: t('Include more tracks from other file'), + category: streamsCategory, + }, - // zoomOperationsCategory - timelineZoomIn: { - name: t('Zoom in timeline'), - category: zoomOperationsCategory, - }, - timelineZoomOut: { - name: t('Zoom out timeline'), - category: zoomOperationsCategory, - }, - timelineToggleComfortZoom: { - name: t('Toggle zoom between 1x and a calculated comfortable zoom level'), - category: zoomOperationsCategory, - }, + // zoomOperationsCategory + timelineZoomIn: { + name: t('Zoom in timeline'), + category: zoomOperationsCategory, + }, + timelineZoomOut: { + name: t('Zoom out timeline'), + category: zoomOperationsCategory, + }, + timelineToggleComfortZoom: { + name: t('Toggle zoom between 1x and a calculated comfortable zoom level'), + category: zoomOperationsCategory, + }, - // outputCategory - export: { - name: t('Export segment(s)'), - category: outputCategory, - }, - captureSnapshot: { - name: t('Capture snapshot'), - category: outputCategory, - }, - captureSnapshotAsCoverArt: { - name: t('Set current frame as cover art'), - category: outputCategory, - }, - extractCurrentSegmentFramesAsImages: { - name: t('Extract frames from current segment as image files'), - category: outputCategory, - }, - extractSelectedSegmentsFramesAsImages: { - name: t('Extract frames from selected segments as image files'), - category: outputCategory, - }, - cleanupFilesDialog: { - name: t('Delete source file'), - category: outputCategory, - }, - convertFormatBatch: { - name: t('Batch convert files to supported format'), - category: outputCategory, - }, - convertFormatCurrentFile: { - name: t('Convert current file to supported format'), - category: outputCategory, - }, - fixInvalidDuration: { - name: t('Fix incorrect duration'), - category: outputCategory, - }, + // outputCategory + export: { + name: t('Export segment(s)'), + category: outputCategory, + }, + captureSnapshot: { + name: t('Capture snapshot'), + category: outputCategory, + }, + captureSnapshotAsCoverArt: { + name: t('Set current frame as cover art'), + category: outputCategory, + }, + extractCurrentSegmentFramesAsImages: { + name: t('Extract frames from current segment as image files'), + category: outputCategory, + }, + extractSelectedSegmentsFramesAsImages: { + name: t('Extract frames from selected segments as image files'), + category: outputCategory, + }, + cleanupFilesDialog: { + name: t('Delete source file'), + category: outputCategory, + }, + convertFormatBatch: { + name: t('Batch convert files to supported format'), + category: outputCategory, + }, + convertFormatCurrentFile: { + name: t('Convert current file to supported format'), + category: outputCategory, + }, + fixInvalidDuration: { + name: t('Fix incorrect duration'), + category: outputCategory, + }, - // batchFilesCategory - batchPreviousFile: { - name: t('Previous file'), - category: batchFilesCategory, - }, - batchNextFile: { - name: t('Next file'), - category: batchFilesCategory, - }, - batchOpenSelectedFile: { - name: t('Open selected file'), - category: batchFilesCategory, - }, - closeBatch: { - name: t('Close batch'), - category: batchFilesCategory, - }, - concatBatch: { - name: t('Merge/concatenate files'), - category: batchFilesCategory, - }, + // batchFilesCategory + batchPreviousFile: { + name: t('Previous file'), + category: batchFilesCategory, + }, + batchOpenPreviousFile: { + name: t('Open previous file'), + category: batchFilesCategory, + }, + batchNextFile: { + name: t('Next file'), + category: batchFilesCategory, + }, + batchOpenNextFile: { + name: t('Open next file'), + category: batchFilesCategory, + }, + batchOpenSelectedFile: { + name: t('Open selected file'), + category: batchFilesCategory, + }, + closeBatch: { + name: t('Close batch'), + category: batchFilesCategory, + }, + concatBatch: { + name: t('Merge/concatenate files'), + category: batchFilesCategory, + }, - // otherCategory - toggleKeyframeCutMode: { - name: t('Cut mode'), - category: otherCategory, - }, - toggleCaptureFormat: { - name: t('Capture frame format'), - category: otherCategory, - }, - toggleStripAudio: { - name: t('Keep or discard audio tracks'), - category: otherCategory, - }, - toggleStripThumbnail: { - name: t('Keep or discard thumbnail tracks'), - category: otherCategory, - }, - increaseRotation: { - name: t('Change rotation'), - category: otherCategory, - }, - setStartTimeOffset: { - name: t('Set custom start offset/timecode'), - category: otherCategory, - }, - undo: { - name: t('Undo'), - category: otherCategory, - }, - redo: { - name: t('Redo'), - category: otherCategory, - }, - copySegmentsToClipboard: { - name: t('Copy selected segments times to clipboard'), - category: otherCategory, - }, - toggleWaveformMode: { - name: t('Show waveform'), - category: otherCategory, - }, - toggleShowThumbnails: { - name: t('Show thumbnails'), - category: otherCategory, - }, - toggleShowKeyframes: { - name: t('Show keyframes'), - category: otherCategory, - }, - toggleFullscreenVideo: { - name: 'Toggle full screen video', - category: otherCategory, - }, - toggleSettings: { - name: t('Settings'), - category: otherCategory, - }, - openSendReportDialog: { - name: t('Report an error'), - category: otherCategory, - }, - openFilesDialog: { - name: t('Open'), - category: otherCategory, - }, - exportYouTube: { - name: t('Start times as YouTube Chapters'), - category: otherCategory, - }, - closeActiveScreen: { - name: t('Close current screen'), - category: otherCategory, - }, - closeCurrentFile: { - name: t('Close current file'), - category: otherCategory, - }, - quit: { - name: t('Quit LosslessCut'), - category: otherCategory, - }, - } satisfies ActionsMap, + // otherCategory + toggleKeyframeCutMode: { + name: t('Cut mode'), + category: otherCategory, + }, + toggleCaptureFormat: { + name: t('Capture frame format'), + category: otherCategory, + }, + toggleStripAudio: { + name: t('Keep or discard audio tracks'), + category: otherCategory, + }, + toggleStripThumbnail: { + name: t('Keep or discard thumbnail tracks'), + category: otherCategory, + }, + increaseRotation: { + name: t('Change rotation'), + category: otherCategory, + }, + setStartTimeOffset: { + name: t('Set custom start offset/timecode'), + category: otherCategory, + }, + undo: { + name: t('Undo'), + category: otherCategory, + }, + redo: { + name: t('Redo'), + category: otherCategory, + }, + copySegmentsToClipboard: { + name: t('Copy selected segments times to clipboard'), + category: otherCategory, + }, + toggleWaveformMode: { + name: t('Show waveform'), + category: otherCategory, + }, + toggleShowThumbnails: { + name: t('Show thumbnails'), + category: otherCategory, + }, + toggleShowKeyframes: { + name: t('Show keyframes'), + category: otherCategory, + }, + toggleFullscreenVideo: { + name: 'Toggle full screen video', + category: otherCategory, + }, + toggleSettings: { + name: t('Settings'), + category: otherCategory, + }, + openSendReportDialog: { + name: t('Report an error'), + category: otherCategory, + }, + openFilesDialog: { + name: t('Open'), + category: otherCategory, + }, + exportYouTube: { + name: t('Start times as YouTube Chapters'), + category: otherCategory, + }, + closeActiveScreen: { + name: t('Close current screen'), + category: otherCategory, + }, + closeCurrentFile: { + name: t('Close current file'), + category: otherCategory, + }, + quit: { + name: t('Quit LosslessCut'), + category: otherCategory, + }, + }; + + // eslint-disable-next-line no-shadow + const extraLinesPerCategory: Record = { + [zoomOperationsCategory]: [ +
+ {t('Zoom in/out timeline')} +
+ + {t('Mouse scroll/wheel up/down')} +
, + +
+ {t('Pan timeline')} +
+ {getModifier(mouseWheelZoomModifierKey).map((v) => {v})} + + {t('Mouse scroll/wheel up/down')} +
, + ], + }; + + return { + extraLinesPerCategory, + actionsMap, }; }, [currentCutSeg, mouseWheelZoomModifierKey, t]); diff --git a/types.ts b/types.ts index 91120583..a79601a5 100644 --- a/types.ts +++ b/types.ts @@ -1,4 +1,4 @@ -export type KeyboardAction = 'addSegment' | 'togglePlayResetSpeed' | 'togglePlayNoResetSpeed' | 'reducePlaybackRate' | 'reducePlaybackRateMore' | 'increasePlaybackRate' | 'increasePlaybackRateMore' | 'timelineToggleComfortZoom' | 'seekPreviousFrame' | 'seekNextFrame' | 'captureSnapshot' | 'setCutStart' | 'setCutEnd' | 'removeCurrentSegment' | 'cleanupFilesDialog' | 'splitCurrentSegment' | 'increaseRotation' | 'goToTimecode' | 'seekBackwards' | 'seekBackwardsPercent' | 'seekBackwardsPercent' | 'seekBackwardsKeyframe' | 'jumpCutStart' | 'seekForwards' | 'seekForwardsPercent' | 'seekForwardsPercent' | 'seekForwardsKeyframe' | 'jumpCutEnd' | 'jumpTimelineStart' | 'jumpTimelineEnd' | 'jumpFirstSegment' | 'jumpPrevSegment' | 'timelineZoomIn' | 'timelineZoomIn' | 'batchPreviousFile' | 'jumpLastSegment' | 'jumpNextSegment' | 'timelineZoomOut' | 'timelineZoomOut' | 'batchNextFile' | 'batchOpenSelectedFile' | 'undo' | 'undo' | 'redo' | 'redo' | 'copySegmentsToClipboard' | 'copySegmentsToClipboard' | 'toggleFullscreenVideo' | 'labelCurrentSegment' | 'export' | 'toggleKeyboardShortcuts' | 'closeActiveScreen' | 'increaseVolume' | 'decreaseVolume' | 'detectBlackScenes' | 'detectSilentScenes' | 'detectSceneChanges' | 'toggleLastCommands' | 'play' | 'pause' | 'reloadFile' | 'html5ify' | 'togglePlayOnlyCurrentSegment' | 'toggleLoopOnlyCurrentSegment' | 'toggleLoopStartEndOnlyCurrentSegment' | 'toggleLoopSelectedSegments' | 'editCurrentSegmentTags' | 'duplicateCurrentSegment' | 'reorderSegsByStartTime' | 'invertAllSegments' | 'fillSegmentsGaps' | 'shiftAllSegmentTimes' | 'alignSegmentTimesToKeyframes' | 'createSegmentsFromKeyframes' | 'createFixedDurationSegments' | 'createNumSegments' | 'createRandomSegments' | 'shuffleSegments' | 'combineOverlappingSegments' | 'combineSelectedSegments' | 'clearSegments' | 'toggleSegmentsList' | 'selectOnlyCurrentSegment' | 'deselectAllSegments' | 'selectAllSegments' | 'toggleCurrentSegmentSelected' | 'invertSelectedSegments' | 'removeSelectedSegments' | 'toggleStreamsSelector' | 'extractAllStreams' | 'showStreamsSelector' | 'showIncludeExternalStreamsDialog' | 'captureSnapshotAsCoverArt' | 'extractCurrentSegmentFramesAsImages' | 'extractSelectedSegmentsFramesAsImages' | 'convertFormatBatch' | 'convertFormatCurrentFile' | 'fixInvalidDuration' | 'closeBatch' | 'concatBatch' | 'toggleKeyframeCutMode' | 'toggleCaptureFormat' | 'toggleStripAudio' | 'toggleStripThumbnail' | 'setStartTimeOffset' | 'toggleWaveformMode' | 'toggleShowThumbnails' | 'toggleShowKeyframes' | 'toggleSettings' | 'openSendReportDialog' | 'openFilesDialog' | 'exportYouTube' | 'closeCurrentFile' | 'quit'; +export type KeyboardAction = 'addSegment' | 'togglePlayResetSpeed' | 'togglePlayNoResetSpeed' | 'reducePlaybackRate' | 'reducePlaybackRateMore' | 'increasePlaybackRate' | 'increasePlaybackRateMore' | 'timelineToggleComfortZoom' | 'seekPreviousFrame' | 'seekNextFrame' | 'captureSnapshot' | 'setCutStart' | 'setCutEnd' | 'removeCurrentSegment' | 'cleanupFilesDialog' | 'splitCurrentSegment' | 'increaseRotation' | 'goToTimecode' | 'seekBackwards' | 'seekBackwardsPercent' | 'seekBackwardsPercent' | 'seekBackwardsKeyframe' | 'jumpCutStart' | 'seekForwards' | 'seekForwardsPercent' | 'seekForwardsPercent' | 'seekForwardsKeyframe' | 'jumpCutEnd' | 'jumpTimelineStart' | 'jumpTimelineEnd' | 'jumpFirstSegment' | 'jumpPrevSegment' | 'timelineZoomIn' | 'timelineZoomIn' | 'batchPreviousFile' | 'jumpLastSegment' | 'jumpNextSegment' | 'timelineZoomOut' | 'timelineZoomOut' | 'batchNextFile' | 'batchOpenSelectedFile' | 'batchOpenPreviousFile' | 'batchOpenNextFile' | 'undo' | 'undo' | 'redo' | 'redo' | 'copySegmentsToClipboard' | 'copySegmentsToClipboard' | 'toggleFullscreenVideo' | 'labelCurrentSegment' | 'export' | 'toggleKeyboardShortcuts' | 'closeActiveScreen' | 'increaseVolume' | 'decreaseVolume' | 'detectBlackScenes' | 'detectSilentScenes' | 'detectSceneChanges' | 'toggleLastCommands' | 'play' | 'pause' | 'reloadFile' | 'html5ify' | 'togglePlayOnlyCurrentSegment' | 'toggleLoopOnlyCurrentSegment' | 'toggleLoopStartEndOnlyCurrentSegment' | 'toggleLoopSelectedSegments' | 'editCurrentSegmentTags' | 'duplicateCurrentSegment' | 'reorderSegsByStartTime' | 'invertAllSegments' | 'fillSegmentsGaps' | 'shiftAllSegmentTimes' | 'alignSegmentTimesToKeyframes' | 'createSegmentsFromKeyframes' | 'createFixedDurationSegments' | 'createNumSegments' | 'createRandomSegments' | 'shuffleSegments' | 'combineOverlappingSegments' | 'combineSelectedSegments' | 'clearSegments' | 'toggleSegmentsList' | 'selectOnlyCurrentSegment' | 'deselectAllSegments' | 'selectAllSegments' | 'toggleCurrentSegmentSelected' | 'invertSelectedSegments' | 'removeSelectedSegments' | 'toggleStreamsSelector' | 'extractAllStreams' | 'showStreamsSelector' | 'showIncludeExternalStreamsDialog' | 'captureSnapshotAsCoverArt' | 'extractCurrentSegmentFramesAsImages' | 'extractSelectedSegmentsFramesAsImages' | 'convertFormatBatch' | 'convertFormatCurrentFile' | 'fixInvalidDuration' | 'closeBatch' | 'concatBatch' | 'toggleKeyframeCutMode' | 'toggleCaptureFormat' | 'toggleStripAudio' | 'toggleStripThumbnail' | 'setStartTimeOffset' | 'toggleWaveformMode' | 'toggleShowThumbnails' | 'toggleShowKeyframes' | 'toggleSettings' | 'openSendReportDialog' | 'openFilesDialog' | 'exportYouTube' | 'closeCurrentFile' | 'quit'; export interface KeyBinding { keys: string,