feat(mini-player): user-bindable keyboard shortcut to toggle mini player

Adds an 'open-mini-player' entry to the in-app keybindings (Settings →
Shortcuts), default unbound. Pressing the chord from the main window
opens the mini and minimises main; pressing it from the mini hides the
mini and restores main — both paths invoke open_mini_player which
already handles the toggle.

The mini window has its own keydown listener (same pattern as Space /
arrows for play/skip) that reads the binding from useKeybindingsStore.
Binding changes in main propagate to an open mini through the existing
storage-event sync (now also covers psysonic_keybindings) so the user
doesn't need to restart the mini after rebinding.

Localized in all 8 supported locales.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Psychotoxical
2026-04-19 13:05:06 +02:00
parent 8cc115cade
commit 60da17f7cc
12 changed files with 27 additions and 1 deletions
+11
View File
@@ -5,6 +5,7 @@ import { Play, Pause, SkipBack, SkipForward, Pin, PinOff, Maximize2, X, ListMusi
import CachedImage from './CachedImage';
import { buildCoverArtUrl, coverArtCacheKey } from '../api/subsonic';
import { usePlayerStore } from '../store/playerStore';
import { useKeybindingsStore, matchInAppBinding } from '../store/keybindingsStore';
import { useDragDrop } from '../contexts/DragDropContext';
import MiniContextMenu from './MiniContextMenu';
import type { MiniSyncPayload, MiniControlAction, MiniTrackInfo } from '../utils/miniPlayerBridge';
@@ -178,11 +179,21 @@ export default function MiniPlayer() {
}, [alwaysOnTop]);
// Keyboard: Space → toggle, ← / → → prev / next. Ignore when typing.
// Also honour the user-configured 'open-mini-player' shortcut so the
// same chord that opens the mini from main also closes it from here.
useEffect(() => {
const onKey = (e: KeyboardEvent) => {
const tgt = e.target as HTMLElement | null;
const tag = tgt?.tagName;
if (tag === 'INPUT' || tag === 'TEXTAREA' || tgt?.isContentEditable) return;
const openMiniBinding = useKeybindingsStore.getState().bindings['open-mini-player'];
if (matchInAppBinding(e, openMiniBinding)) {
e.preventDefault();
invoke('open_mini_player').catch(() => {});
return;
}
if (e.key === ' ' || e.code === 'Space') {
e.preventDefault();
emit('mini:control', 'toggle').catch(() => {});