feat(player): queue undo/redo with hotkeys and playback-aware restore

Snapshot queue, current track, time, and pause before queue mutations.
Ctrl+Z/Cmd+Z restores prior state; Ctrl+Shift+Z/Cmd+Shift+Z reapplies undone
edits when the redo stack is non-empty; new edits clear redo.

Resync the Rust audio engine only when the current song identity changes
from the pre-apply state so reorder/enqueue-style edits keep live playback.

Register the document capture listener from main.tsx after the window label
is set. Mini player forwards undo/redo via mini:undo-queue and mini:redo-queue.
This commit is contained in:
Maxim Isaev
2026-04-27 00:17:10 +03:00
parent e0c53da94d
commit 39f4d03da5
5 changed files with 408 additions and 7 deletions
+11 -3
View File
@@ -93,7 +93,13 @@ import { initHotCachePrefetch } from './hotCachePrefetch';
import i18n from './i18n';
import { playByOpaqueId } from './utils/playByOpaqueId';
import { switchActiveServer } from './utils/switchActiveServer';
import { usePlayerStore, initAudioListeners, songToTrack, shuffleArray, flushPlayQueuePosition } from './store/playerStore';
import {
usePlayerStore,
initAudioListeners,
songToTrack,
shuffleArray,
flushPlayQueuePosition,
} from './store/playerStore';
import { useThemeStore } from './store/themeStore';
import { useThemeScheduler } from './hooks/useThemeScheduler';
import { useFontStore } from './store/fontStore';
@@ -858,8 +864,10 @@ function TauriEventBridge() {
// Configurable keybindings
useEffect(() => {
const onKey = (e: KeyboardEvent) => {
const tag = (e.target as HTMLElement)?.tagName;
if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;
const el = e.target as HTMLElement;
const tag = el?.tagName;
const editable = Boolean(el?.isContentEditable);
if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || editable) return;
const chord = buildInAppBinding(e);
if (chord) {