feat: complete Phase 13 - Keyboard Shortcuts

Added all remaining keyboard shortcuts for a complete keyboard-driven workflow:

Playback Shortcuts:
- Home: Jump to start
- End: Jump to end
- Left/Right Arrow: Seek ±1 second
- Ctrl+Left/Right: Seek ±5 seconds

Editing Shortcuts:
- Ctrl+A: Select All (entire track content)

View Shortcuts:
- Ctrl+Plus/Equals: Zoom in
- Ctrl+Minus: Zoom out
- Ctrl+0: Fit to view

All shortcuts work seamlessly with existing shortcuts:
- Spacebar: Play/Pause
- Ctrl+Z/Y: Undo/Redo
- Ctrl+X/C/V: Cut/Copy/Paste
- Ctrl+S: Save
- Ctrl+D: Duplicate
- Delete/Backspace: Delete
- Escape: Clear selection

The editor is now fully controllable via keyboard for a professional
audio editing workflow similar to Audacity/Reaper.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-19 10:36:58 +01:00
parent 7de75f7b2b
commit ac8aa9e6c6
2 changed files with 106 additions and 26 deletions

View File

@@ -1386,12 +1386,90 @@ export function AudioEditor() {
if (e.key === 'Escape') {
e.preventDefault();
setSelectedTrackId(null);
return;
}
// Home: Go to start
if (e.key === 'Home') {
e.preventDefault();
seek(0);
return;
}
// End: Go to end
if (e.key === 'End') {
e.preventDefault();
seek(duration);
return;
}
// Left Arrow: Seek backward 1 second
if (e.key === 'ArrowLeft' && !e.ctrlKey && !e.metaKey) {
e.preventDefault();
seek(Math.max(0, currentTime - 1));
return;
}
// Right Arrow: Seek forward 1 second
if (e.key === 'ArrowRight' && !e.ctrlKey && !e.metaKey) {
e.preventDefault();
seek(Math.min(duration, currentTime + 1));
return;
}
// Ctrl+Left Arrow: Seek backward 5 seconds
if (e.key === 'ArrowLeft' && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
seek(Math.max(0, currentTime - 5));
return;
}
// Ctrl+Right Arrow: Seek forward 5 seconds
if (e.key === 'ArrowRight' && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
seek(Math.min(duration, currentTime + 5));
return;
}
// Ctrl+A: Select All (select all content on current track)
if ((e.ctrlKey || e.metaKey) && e.key === 'a') {
e.preventDefault();
if (selectedTrackId) {
const track = tracks.find(t => t.id === selectedTrackId);
if (track?.audioBuffer) {
updateTrack(selectedTrackId, {
selection: { start: 0, end: track.audioBuffer.duration }
});
}
}
return;
}
// Ctrl+Plus/Equals: Zoom in
if ((e.ctrlKey || e.metaKey) && (e.key === '+' || e.key === '=')) {
e.preventDefault();
handleZoomIn();
return;
}
// Ctrl+Minus: Zoom out
if ((e.ctrlKey || e.metaKey) && e.key === '-') {
e.preventDefault();
handleZoomOut();
return;
}
// Ctrl+0: Fit to view
if ((e.ctrlKey || e.metaKey) && e.key === '0') {
e.preventDefault();
handleFitToView();
return;
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [togglePlayPause, canUndo, canRedo, undo, redo, handleCut, handleCopy, handlePaste, handleDelete, handleDuplicate, handleSaveProject]);
}, [togglePlayPause, canUndo, canRedo, undo, redo, handleCut, handleCopy, handlePaste, handleDelete, handleDuplicate, handleSaveProject, seek, duration, currentTime, selectedTrackId, tracks, updateTrack, handleZoomIn, handleZoomOut, handleFitToView]);
return (
<>