Add hotkeys manager
- Add configuration to settings store - Initialize global hotkeys on startup from renderer
This commit is contained in:
parent
6056504f00
commit
d7f24262fd
7 changed files with 432 additions and 12 deletions
|
@ -27,7 +27,7 @@ import MpvAPI from 'node-mpv';
|
|||
import { disableMediaKeys, enableMediaKeys } from './features/core/player/media-keys';
|
||||
import { store } from './features/core/settings/index';
|
||||
import MenuBuilder from './menu';
|
||||
import { isLinux, isMacOS, isWindows, resolveHtmlPath } from './utils';
|
||||
import { hotkeyToElectronAccelerator, isLinux, isMacOS, isWindows, resolveHtmlPath } from './utils';
|
||||
import './features';
|
||||
|
||||
declare module 'node-mpv';
|
||||
|
@ -97,7 +97,6 @@ export const getMainWindow = () => {
|
|||
|
||||
const createWinThumbarButtons = () => {
|
||||
if (isWindows()) {
|
||||
console.log('setting buttons');
|
||||
getMainWindow()?.setThumbarButtons([
|
||||
{
|
||||
click: () => getMainWindow()?.webContents.send('renderer-player-previous'),
|
||||
|
@ -308,7 +307,6 @@ const createWindow = async () => {
|
|||
app.commandLine.appendSwitch('disable-features', 'HardwareMediaKeyHandling,MediaSessionService');
|
||||
|
||||
const MPV_BINARY_PATH = store.get('mpv_path') as string | undefined;
|
||||
const MPV_PARAMETERS = store.get('mpv_parameters') as Array<string> | undefined;
|
||||
|
||||
const prefetchPlaylistParams = [
|
||||
'--prefetch-playlist=no',
|
||||
|
@ -316,10 +314,10 @@ const prefetchPlaylistParams = [
|
|||
'--prefetch-playlist',
|
||||
];
|
||||
|
||||
const DEFAULT_MPV_PARAMETERS = () => {
|
||||
const DEFAULT_MPV_PARAMETERS = (extraParameters?: string[]) => {
|
||||
const parameters = [];
|
||||
|
||||
if (!MPV_PARAMETERS?.some((param) => prefetchPlaylistParams.includes(param))) {
|
||||
if (!extraParameters?.some((param) => prefetchPlaylistParams.includes(param))) {
|
||||
parameters.push('--prefetch-playlist=yes');
|
||||
}
|
||||
|
||||
|
@ -331,6 +329,8 @@ let mpvInstance: MpvAPI | null = null;
|
|||
const createMpv = (data: { extraParameters?: string[]; properties?: Record<string, any> }) => {
|
||||
const { extraParameters, properties } = data;
|
||||
|
||||
const params = uniq([...DEFAULT_MPV_PARAMETERS(extraParameters), ...(extraParameters || [])]);
|
||||
|
||||
mpvInstance = new MpvAPI(
|
||||
{
|
||||
audio_only: true,
|
||||
|
@ -338,9 +338,7 @@ const createMpv = (data: { extraParameters?: string[]; properties?: Record<strin
|
|||
binary: MPV_BINARY_PATH || '',
|
||||
time_update: 1,
|
||||
},
|
||||
MPV_PARAMETERS || extraParameters
|
||||
? uniq([...DEFAULT_MPV_PARAMETERS(), ...(MPV_PARAMETERS || []), ...(extraParameters || [])])
|
||||
: DEFAULT_MPV_PARAMETERS(),
|
||||
params,
|
||||
);
|
||||
|
||||
mpvInstance.setMultipleProperties(properties || {});
|
||||
|
@ -402,6 +400,91 @@ ipcMain.on(
|
|||
},
|
||||
);
|
||||
|
||||
ipcMain.on(
|
||||
'player-initialize',
|
||||
async (_event, data: { extraParameters?: string[]; properties?: Record<string, any> }) => {
|
||||
createMpv(data);
|
||||
},
|
||||
);
|
||||
|
||||
// Must duplicate with the one in renderer process settings.store.ts
|
||||
enum BindingActions {
|
||||
GLOBAL_SEARCH = 'globalSearch',
|
||||
LOCAL_SEARCH = 'localSearch',
|
||||
MUTE = 'volumeMute',
|
||||
NEXT = 'next',
|
||||
PAUSE = 'pause',
|
||||
PLAY = 'play',
|
||||
PLAY_PAUSE = 'playPause',
|
||||
PREVIOUS = 'previous',
|
||||
SHUFFLE = 'toggleShuffle',
|
||||
SKIP_BACKWARD = 'skipBackward',
|
||||
SKIP_FORWARD = 'skipForward',
|
||||
STOP = 'stop',
|
||||
TOGGLE_FULLSCREEN_PLAYER = 'toggleFullscreenPlayer',
|
||||
TOGGLE_QUEUE = 'toggleQueue',
|
||||
TOGGLE_REPEAT = 'toggleRepeat',
|
||||
VOLUME_DOWN = 'volumeDown',
|
||||
VOLUME_UP = 'volumeUp',
|
||||
}
|
||||
|
||||
const HOTKEY_ACTIONS: Record<BindingActions, () => void> = {
|
||||
[BindingActions.MUTE]: () => getMainWindow()?.webContents.send('renderer-player-volume-mute'),
|
||||
[BindingActions.NEXT]: () => getMainWindow()?.webContents.send('renderer-player-next'),
|
||||
[BindingActions.PAUSE]: () => getMainWindow()?.webContents.send('renderer-player-pause'),
|
||||
[BindingActions.PLAY]: () => getMainWindow()?.webContents.send('renderer-player-play'),
|
||||
[BindingActions.PLAY_PAUSE]: () =>
|
||||
getMainWindow()?.webContents.send('renderer-player-play-pause'),
|
||||
[BindingActions.PREVIOUS]: () => getMainWindow()?.webContents.send('renderer-player-previous'),
|
||||
[BindingActions.SHUFFLE]: () =>
|
||||
getMainWindow()?.webContents.send('renderer-player-toggle-shuffle'),
|
||||
[BindingActions.SKIP_BACKWARD]: () =>
|
||||
getMainWindow()?.webContents.send('renderer-player-skip-backward'),
|
||||
[BindingActions.SKIP_FORWARD]: () =>
|
||||
getMainWindow()?.webContents.send('renderer-player-skip-forward'),
|
||||
[BindingActions.STOP]: () => getMainWindow()?.webContents.send('renderer-player-stop'),
|
||||
[BindingActions.TOGGLE_REPEAT]: () =>
|
||||
getMainWindow()?.webContents.send('renderer-player-toggle-repeat'),
|
||||
[BindingActions.VOLUME_UP]: () => getMainWindow()?.webContents.send('renderer-player-volume-up'),
|
||||
[BindingActions.VOLUME_DOWN]: () =>
|
||||
getMainWindow()?.webContents.send('renderer-player-volume-down'),
|
||||
[BindingActions.GLOBAL_SEARCH]: () => {},
|
||||
[BindingActions.LOCAL_SEARCH]: () => {},
|
||||
[BindingActions.TOGGLE_QUEUE]: () => {},
|
||||
[BindingActions.TOGGLE_FULLSCREEN_PLAYER]: () => {},
|
||||
};
|
||||
|
||||
ipcMain.on(
|
||||
'set-global-shortcuts',
|
||||
(
|
||||
_event,
|
||||
data: Record<BindingActions, { allowGlobal: boolean; hotkey: string; isGlobal: boolean }>,
|
||||
) => {
|
||||
// Since we're not tracking the previous shortcuts, we need to unregister all of them
|
||||
globalShortcut.unregisterAll();
|
||||
|
||||
for (const shortcut of Object.keys(data)) {
|
||||
const isGlobalHotkey = data[shortcut as BindingActions].isGlobal;
|
||||
const isValidHotkey =
|
||||
data[shortcut as BindingActions].hotkey && data[shortcut as BindingActions].hotkey !== '';
|
||||
|
||||
if (isGlobalHotkey && isValidHotkey) {
|
||||
const accelerator = hotkeyToElectronAccelerator(data[shortcut as BindingActions].hotkey);
|
||||
|
||||
globalShortcut.register(accelerator, () => {
|
||||
HOTKEY_ACTIONS[shortcut as BindingActions]();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const globalMediaKeysEnabled = store.get('global_media_hotkeys') as boolean;
|
||||
|
||||
if (globalMediaKeysEnabled) {
|
||||
enableMediaKeys(mainWindow);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
app.on('before-quit', () => {
|
||||
getMpvInstance()?.stop();
|
||||
});
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import { ipcRenderer, IpcRendererEvent } from 'electron';
|
||||
import { PlayerData } from '/@/renderer/store';
|
||||
|
||||
const initialize = (data: { extraParameters?: string[]; properties?: Record<string, any> }) => {
|
||||
ipcRenderer.send('player-initialize', data);
|
||||
};
|
||||
|
||||
const restart = (data: { extraParameters?: string[]; properties?: Record<string, any> }) => {
|
||||
ipcRenderer.send('player-restart', data);
|
||||
};
|
||||
|
@ -98,6 +102,34 @@ const rendererStop = (cb: (event: IpcRendererEvent, data: PlayerData) => void) =
|
|||
ipcRenderer.on('renderer-player-stop', cb);
|
||||
};
|
||||
|
||||
const rendererSkipForward = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
|
||||
ipcRenderer.on('renderer-player-skip-forward', cb);
|
||||
};
|
||||
|
||||
const rendererSkipBackward = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
|
||||
ipcRenderer.on('renderer-player-skip-backward', cb);
|
||||
};
|
||||
|
||||
const rendererVolumeUp = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
|
||||
ipcRenderer.on('renderer-player-volume-up', cb);
|
||||
};
|
||||
|
||||
const rendererVolumeDown = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
|
||||
ipcRenderer.on('renderer-player-volume-down', cb);
|
||||
};
|
||||
|
||||
const rendererVolumeMute = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
|
||||
ipcRenderer.on('renderer-player-volume-mute', cb);
|
||||
};
|
||||
|
||||
const rendererToggleRepeat = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
|
||||
ipcRenderer.on('renderer-player-toggle-repeat', cb);
|
||||
};
|
||||
|
||||
const rendererToggleShuffle = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
|
||||
ipcRenderer.on('renderer-player-toggle-shuffle', cb);
|
||||
};
|
||||
|
||||
const rendererQuit = (cb: (event: IpcRendererEvent) => void) => {
|
||||
ipcRenderer.on('renderer-player-quit', cb);
|
||||
};
|
||||
|
@ -105,6 +137,7 @@ const rendererQuit = (cb: (event: IpcRendererEvent) => void) => {
|
|||
export const mpvPlayer = {
|
||||
autoNext,
|
||||
currentTime,
|
||||
initialize,
|
||||
mute,
|
||||
next,
|
||||
pause,
|
||||
|
@ -130,5 +163,12 @@ export const mpvPlayerListener = {
|
|||
rendererPlayPause,
|
||||
rendererPrevious,
|
||||
rendererQuit,
|
||||
rendererSkipBackward,
|
||||
rendererSkipForward,
|
||||
rendererStop,
|
||||
rendererToggleRepeat,
|
||||
rendererToggleShuffle,
|
||||
rendererVolumeDown,
|
||||
rendererVolumeMute,
|
||||
rendererVolumeUp,
|
||||
};
|
||||
|
|
|
@ -29,3 +29,24 @@ export const isWindows = () => {
|
|||
export const isLinux = () => {
|
||||
return process.platform === 'linux';
|
||||
};
|
||||
|
||||
export const hotkeyToElectronAccelerator = (hotkey: string) => {
|
||||
let accelerator = hotkey;
|
||||
|
||||
const replacements = {
|
||||
mod: 'CmdOrCtrl',
|
||||
numpad: 'num',
|
||||
numpadadd: 'numadd',
|
||||
numpaddecimal: 'numdec',
|
||||
numpaddivide: 'numdiv',
|
||||
numpadenter: 'numenter',
|
||||
numpadmultiply: 'nummult',
|
||||
numpadsubtract: 'numsub',
|
||||
};
|
||||
|
||||
Object.keys(replacements).forEach((key) => {
|
||||
accelerator = accelerator.replace(key, replacements[key as keyof typeof replacements]);
|
||||
});
|
||||
|
||||
return accelerator;
|
||||
};
|
||||
|
|
|
@ -8,7 +8,7 @@ import { initSimpleImg } from 'react-simple-img';
|
|||
import { BaseContextModal } from './components';
|
||||
import { useTheme } from './hooks';
|
||||
import { AppRouter } from './router/app-router';
|
||||
import { useSettingsStore } from './store/settings.store';
|
||||
import { useHotkeySettings, useSettingsStore } from './store/settings.store';
|
||||
import './styles/global.scss';
|
||||
import '@ag-grid-community/styles/ag-grid.css';
|
||||
import { ContextMenuProvider } from '/@/renderer/features/context-menu';
|
||||
|
@ -24,11 +24,12 @@ ModuleRegistry.registerModules([ClientSideRowModelModule, InfiniteRowModelModule
|
|||
initSimpleImg({ threshold: 0.05 }, true);
|
||||
|
||||
const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null;
|
||||
const ipc = isElectron() ? window.electron.ipc : null;
|
||||
|
||||
export const App = () => {
|
||||
const theme = useTheme();
|
||||
const contentFont = useSettingsStore((state) => state.general.fontContent);
|
||||
|
||||
const { bindings } = useHotkeySettings();
|
||||
const handlePlayQueueAdd = useHandlePlayQueueAdd();
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -44,12 +45,22 @@ export const App = () => {
|
|||
volume: usePlayerStore.getState().volume,
|
||||
};
|
||||
|
||||
mpvPlayer?.restart({
|
||||
mpvPlayer?.initialize({
|
||||
extraParameters,
|
||||
properties,
|
||||
});
|
||||
|
||||
return () => {
|
||||
mpvPlayer?.quit();
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isElectron()) {
|
||||
ipc?.send('set-global-shortcuts', bindings);
|
||||
}
|
||||
}, [bindings]);
|
||||
|
||||
return (
|
||||
<MantineProvider
|
||||
withGlobalStyles
|
||||
|
|
|
@ -0,0 +1,222 @@
|
|||
import { Group } from '@mantine/core';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { RiDeleteBinLine, RiEditLine, RiKeyboardBoxLine } from 'react-icons/ri';
|
||||
import styled from 'styled-components';
|
||||
import { Button, TextInput, Checkbox } from '/@/renderer/components';
|
||||
import isElectron from 'is-electron';
|
||||
import { BindingActions, useHotkeySettings, useSettingsStoreActions } from '/@/renderer/store';
|
||||
import { SettingsOptions } from '/@/renderer/features/settings/components/settings-option';
|
||||
|
||||
const ipc = isElectron() ? window.electron.ipc : null;
|
||||
|
||||
const BINDINGS_MAP: Record<BindingActions, string> = {
|
||||
globalSearch: 'Global search',
|
||||
localSearch: 'In-page search',
|
||||
next: 'Next track',
|
||||
pause: 'Pause',
|
||||
play: 'Play',
|
||||
playPause: 'Play / Pause',
|
||||
previous: 'Previous track',
|
||||
skipBackward: 'Skip backward',
|
||||
skipForward: 'Skip forward',
|
||||
stop: 'Stop',
|
||||
toggleFullscreenPlayer: 'Toggle fullscreen player',
|
||||
toggleQueue: 'Toggle queue',
|
||||
toggleRepeat: 'Toggle repeat',
|
||||
toggleShuffle: 'Toggle shuffle',
|
||||
volumeDown: 'Volume down',
|
||||
volumeMute: 'Volume mute',
|
||||
volumeUp: 'Volume up',
|
||||
};
|
||||
|
||||
const HotkeysContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
|
||||
button {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
export const HotkeyManagerSettings = () => {
|
||||
const { bindings, globalMediaHotkeys } = useHotkeySettings();
|
||||
const { setSettings } = useSettingsStoreActions();
|
||||
const [selected, setSelected] = useState<BindingActions | null>(null);
|
||||
|
||||
const handleSetHotkey = useCallback(
|
||||
(binding: BindingActions, e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
e.preventDefault();
|
||||
const IGNORED_KEYS = ['Control', 'Alt', 'Shift', 'Meta', ' ', 'Escape'];
|
||||
const keys = [];
|
||||
if (e.ctrlKey) keys.push('mod');
|
||||
if (e.altKey) keys.push('alt');
|
||||
if (e.shiftKey) keys.push('shift');
|
||||
if (e.metaKey) keys.push('meta');
|
||||
if (e.key === ' ') keys.push('space');
|
||||
if (!IGNORED_KEYS.includes(e.key)) {
|
||||
if (e.code.includes('Numpad')) {
|
||||
if (e.key === '+') keys.push('numpadadd');
|
||||
else if (e.key === '-') keys.push('numpadsubtract');
|
||||
else if (e.key === '*') keys.push('numpadmultiply');
|
||||
else if (e.key === '/') keys.push('numpaddivide');
|
||||
else if (e.key === '.') keys.push('numpaddecimal');
|
||||
else keys.push(`numpad${e.key}`.toLowerCase());
|
||||
} else {
|
||||
keys.push(e.key?.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
const bindingString = keys.join('+');
|
||||
|
||||
const updatedBindings = {
|
||||
...bindings,
|
||||
[binding]: { ...bindings[binding], hotkey: bindingString },
|
||||
};
|
||||
|
||||
setSettings({
|
||||
hotkeys: {
|
||||
bindings: updatedBindings,
|
||||
globalMediaHotkeys,
|
||||
},
|
||||
});
|
||||
|
||||
ipc?.send('set-global-shortcuts', updatedBindings);
|
||||
},
|
||||
[bindings, globalMediaHotkeys, setSettings],
|
||||
);
|
||||
|
||||
const handleSetGlobalHotkey = useCallback(
|
||||
(binding: BindingActions, e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const updatedBindings = {
|
||||
...bindings,
|
||||
[binding]: { ...bindings[binding], isGlobal: e.currentTarget.checked },
|
||||
};
|
||||
|
||||
console.log('updatedBindings :>> ', updatedBindings);
|
||||
|
||||
setSettings({
|
||||
hotkeys: {
|
||||
bindings: updatedBindings,
|
||||
globalMediaHotkeys,
|
||||
},
|
||||
});
|
||||
|
||||
ipc?.send('set-global-shortcuts', updatedBindings);
|
||||
},
|
||||
[bindings, globalMediaHotkeys, setSettings],
|
||||
);
|
||||
|
||||
const handleClearHotkey = useCallback(
|
||||
(binding: BindingActions) => {
|
||||
const updatedBindings = {
|
||||
...bindings,
|
||||
[binding]: { ...bindings[binding], hotkey: '', isGlobal: false },
|
||||
};
|
||||
|
||||
setSettings({
|
||||
hotkeys: {
|
||||
bindings: updatedBindings,
|
||||
globalMediaHotkeys,
|
||||
},
|
||||
});
|
||||
|
||||
ipc?.send('set-global-shortcuts', updatedBindings);
|
||||
},
|
||||
[bindings, globalMediaHotkeys, setSettings],
|
||||
);
|
||||
|
||||
const duplicateHotkeyMap = useMemo(() => {
|
||||
const countPerHotkey = Object.values(bindings).reduce((acc, key) => {
|
||||
const hotkey = key.hotkey;
|
||||
if (!hotkey) return acc;
|
||||
|
||||
if (acc[hotkey]) {
|
||||
acc[hotkey] += 1;
|
||||
} else {
|
||||
acc[hotkey] = 1;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {} as Record<string, number>);
|
||||
|
||||
const duplicateKeys = Object.keys(countPerHotkey).filter((key) => countPerHotkey[key] > 1);
|
||||
|
||||
return duplicateKeys;
|
||||
}, [bindings]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingsOptions
|
||||
control={<></>}
|
||||
description="Configure application hotkeys. Toggle the checkbox to set as a global hotkey (desktop only)"
|
||||
title="Application hotkeys"
|
||||
/>
|
||||
<HotkeysContainer>
|
||||
{Object.keys(bindings)
|
||||
.filter((binding) => BINDINGS_MAP[binding as keyof typeof BINDINGS_MAP])
|
||||
.map((binding) => (
|
||||
<Group
|
||||
key={`hotkey-${binding}`}
|
||||
noWrap
|
||||
>
|
||||
<TextInput
|
||||
readOnly
|
||||
style={{ userSelect: 'none' }}
|
||||
value={BINDINGS_MAP[binding as keyof typeof BINDINGS_MAP]}
|
||||
/>
|
||||
<TextInput
|
||||
readOnly
|
||||
icon={<RiKeyboardBoxLine />}
|
||||
id={`hotkey-${binding}`}
|
||||
style={{
|
||||
opacity: selected === (binding as BindingActions) ? 0.8 : 1,
|
||||
outline: duplicateHotkeyMap.includes(
|
||||
bindings[binding as keyof typeof BINDINGS_MAP].hotkey!,
|
||||
)
|
||||
? '1px dashed red'
|
||||
: undefined,
|
||||
}}
|
||||
value={bindings[binding as keyof typeof BINDINGS_MAP].hotkey}
|
||||
onBlur={() => setSelected(null)}
|
||||
onChange={() => {}}
|
||||
onKeyDownCapture={(e) => {
|
||||
if (selected !== (binding as BindingActions)) return;
|
||||
handleSetHotkey(binding as BindingActions, e);
|
||||
}}
|
||||
/>
|
||||
{isElectron() && (
|
||||
<Checkbox
|
||||
checked={bindings[binding as keyof typeof BINDINGS_MAP].isGlobal}
|
||||
disabled={bindings[binding as keyof typeof BINDINGS_MAP].hotkey === ''}
|
||||
size="xl"
|
||||
style={{
|
||||
opacity: bindings[binding as keyof typeof BINDINGS_MAP].allowGlobal ? 1 : 0,
|
||||
}}
|
||||
onChange={(e) => handleSetGlobalHotkey(binding as BindingActions, e)}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
variant="default"
|
||||
w={100}
|
||||
onClick={() => {
|
||||
setSelected(binding as BindingActions);
|
||||
document.getElementById(`hotkey-${binding}`)?.focus();
|
||||
}}
|
||||
>
|
||||
<RiEditLine />
|
||||
</Button>
|
||||
<Button
|
||||
variant="default"
|
||||
onClick={() => handleClearHotkey(binding as BindingActions)}
|
||||
>
|
||||
<RiDeleteBinLine />
|
||||
</Button>
|
||||
</Group>
|
||||
))}
|
||||
</HotkeysContainer>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,10 +1,13 @@
|
|||
import { Stack } from '@mantine/core';
|
||||
import { Divider, Stack } from '@mantine/core';
|
||||
import { WindowHotkeySettings } from './window-hotkey-settings';
|
||||
import { HotkeyManagerSettings } from '/@/renderer/features/settings/components/hotkeys/hotkey-manager-settings';
|
||||
|
||||
export const HotkeysTab = () => {
|
||||
return (
|
||||
<Stack spacing="md">
|
||||
<WindowHotkeySettings />
|
||||
<Divider />
|
||||
<HotkeyManagerSettings />
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -42,6 +42,26 @@ type MpvSettings = {
|
|||
replayGainPreampDB?: number;
|
||||
};
|
||||
|
||||
export enum BindingActions {
|
||||
GLOBAL_SEARCH = 'globalSearch',
|
||||
LOCAL_SEARCH = 'localSearch',
|
||||
MUTE = 'volumeMute',
|
||||
NEXT = 'next',
|
||||
PAUSE = 'pause',
|
||||
PLAY = 'play',
|
||||
PLAY_PAUSE = 'playPause',
|
||||
PREVIOUS = 'previous',
|
||||
SHUFFLE = 'toggleShuffle',
|
||||
SKIP_BACKWARD = 'skipBackward',
|
||||
SKIP_FORWARD = 'skipForward',
|
||||
STOP = 'stop',
|
||||
TOGGLE_FULLSCREEN_PLAYER = 'toggleFullscreenPlayer',
|
||||
TOGGLE_QUEUE = 'toggleQueue',
|
||||
TOGGLE_REPEAT = 'toggleRepeat',
|
||||
VOLUME_DOWN = 'volumeDown',
|
||||
VOLUME_UP = 'volumeUp',
|
||||
}
|
||||
|
||||
export interface SettingsState {
|
||||
general: {
|
||||
followSystemTheme: boolean;
|
||||
|
@ -60,6 +80,7 @@ export interface SettingsState {
|
|||
volumeWheelStep: number;
|
||||
};
|
||||
hotkeys: {
|
||||
bindings: Record<BindingActions, { allowGlobal: boolean; hotkey: string; isGlobal: boolean }>;
|
||||
globalMediaHotkeys: boolean;
|
||||
};
|
||||
playback: {
|
||||
|
@ -118,6 +139,25 @@ const initialState: SettingsState = {
|
|||
volumeWheelStep: 5,
|
||||
},
|
||||
hotkeys: {
|
||||
bindings: {
|
||||
globalSearch: { allowGlobal: false, hotkey: 'mod+k', isGlobal: false },
|
||||
localSearch: { allowGlobal: false, hotkey: 'mod+f', isGlobal: false },
|
||||
next: { allowGlobal: true, hotkey: '', isGlobal: false },
|
||||
pause: { allowGlobal: true, hotkey: '', isGlobal: false },
|
||||
play: { allowGlobal: true, hotkey: '', isGlobal: false },
|
||||
playPause: { allowGlobal: true, hotkey: '', isGlobal: false },
|
||||
previous: { allowGlobal: true, hotkey: '', isGlobal: false },
|
||||
skipBackward: { allowGlobal: true, hotkey: '', isGlobal: false },
|
||||
skipForward: { allowGlobal: true, hotkey: '', isGlobal: false },
|
||||
stop: { allowGlobal: true, hotkey: '', isGlobal: false },
|
||||
toggleFullscreenPlayer: { allowGlobal: false, hotkey: '', isGlobal: false },
|
||||
toggleQueue: { allowGlobal: false, hotkey: '', isGlobal: false },
|
||||
toggleRepeat: { allowGlobal: true, hotkey: '', isGlobal: false },
|
||||
toggleShuffle: { allowGlobal: true, hotkey: '', isGlobal: false },
|
||||
volumeDown: { allowGlobal: true, hotkey: '', isGlobal: false },
|
||||
volumeMute: { allowGlobal: true, hotkey: '', isGlobal: false },
|
||||
volumeUp: { allowGlobal: true, hotkey: '', isGlobal: false },
|
||||
},
|
||||
globalMediaHotkeys: false,
|
||||
},
|
||||
playback: {
|
||||
|
|
Reference in a new issue