music on atproto
plyr.fm
1# keyboard shortcuts
2
3global keyboard shortcuts for the plyr.fm frontend.
4
5## implementation
6
7shortcuts are handled in the root layout (`frontend/src/routes/+layout.svelte`) with context-aware filtering to avoid conflicts with form inputs and other interactive elements.
8
9## available shortcuts
10
11### Cmd/Ctrl+K - open search
12
13**location**: `frontend/src/routes/+layout.svelte`
14
15opens the unified search modal for searching tracks, artists, albums, and tags.
16
17**behavior**:
18- **Cmd+K** on macOS, **Ctrl+K** on windows/linux
19- works from anywhere, including input fields (uses modifier key)
20- toggles search modal open/closed
21- focuses search input automatically on open
22
23**in-modal navigation**:
24- **arrow up/down** - navigate results
25- **enter** - select highlighted result
26- **esc** - close modal
27
28see [search.md](./search.md) for full documentation.
29
30---
31
32### Q - toggle queue
33
34**location**: `frontend/src/routes/+layout.svelte`
35
36toggles the queue sidebar visibility.
37
38**behavior**:
39- works from any page in the app
40- ignores when modifier keys are pressed (cmd/ctrl/alt)
41- skips when focus is in input/textarea/contenteditable elements
42- persists visibility state to localStorage
43
44**desktop**: queue opens as 360px fixed sidebar, content shifts left via `margin-right: 360px`
45**mobile**: queue opens as full-screen overlay (no content shift)
46
47**accessibility**:
48- toggle button includes `aria-pressed={showQueue}`
49- `aria-label="toggle queue (Q)"` for screen readers
50- tooltip shows keyboard hint
51
52---
53
54## playback shortcuts
55
56all playback shortcuts require a track to be loaded and are disabled when the search modal is open.
57
58### Space - play/pause
59
60toggles playback of the current track.
61
62### Left Arrow - seek backward
63
64seeks backward 10 seconds in the current track.
65
66### Right Arrow - seek forward
67
68seeks forward 10 seconds in the current track.
69
70### J - previous track
71
72goes to the previous track in the queue. if more than 3 seconds into the current track, restarts it instead.
73
74### L - next track
75
76advances to the next track in the queue (if available).
77
78### M - mute/unmute
79
80toggles mute. restores previous volume level when unmuting.
81
82---
83
84## adding new shortcuts
85
86when adding keyboard shortcuts:
87
881. **add handler to root layout** - keeps all shortcuts in one place
892. **filter contexts** - check for modifier keys and active element
903. **prevent conflicts** - avoid letters used in common form inputs
914. **document behavior** - update this file with key, action, and context rules
925. **add accessibility** - include aria-labels and tooltips mentioning the shortcut
93
94### example pattern
95
96```typescript
97function handleShortcut(event: KeyboardEvent) {
98 // ignore modifiers
99 if (event.metaKey || event.ctrlKey || event.altKey) return;
100
101 // ignore in inputs
102 const target = event.target as HTMLElement;
103 if (
104 target.tagName === 'INPUT' ||
105 target.tagName === 'TEXTAREA' ||
106 target.isContentEditable
107 ) return;
108
109 // handle key
110 if (event.key.toLowerCase() === 'q') {
111 event.preventDefault();
112 // do something
113 }
114}
115
116onMount(() => {
117 window.addEventListener('keydown', handleShortcut);
118});
119
120onDestroy(() => {
121 if (browser) {
122 window.removeEventListener('keydown', handleShortcut);
123 }
124});
125```
126
127## future candidates
128
129potential shortcuts to consider:
130- **/** - focus search (alternative to Cmd/Ctrl+K)
131- **T** - cycle theme (dark/light/system)
132- **S** - shuffle queue
133
134## design principles
135
1361. **global availability** - shortcuts should work from anywhere unless context prevents it
1372. **respect focus** - never intercept input from form fields
1383. **progressive disclosure** - show hints in tooltips and UI
1394. **consistency** - follow common web app conventions where possible
1405. **accessibility first** - keyboard shortcuts enhance but don't replace mouse/touch interaction