Skip to content

Commit 126e39f

Browse files
authored
Merge pull request #61 from saksham-1304/feature/keyboard-shortcuts
feat: Add comprehensive keyboard shortcuts and VS Code-style Command Palette System
2 parents 100cd50 + 821a920 commit 126e39f

10 files changed

Lines changed: 3448 additions & 11 deletions

File tree

KEYBOARD_SHORTCUTS.md

Lines changed: 392 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,392 @@
1+
# Keyboard Shortcuts & Command Palette
2+
3+
## Overview
4+
5+
ResCanvas now features a comprehensive keyboard shortcuts system and VS Code-style command palette for power users to work efficiently without mouse/toolbar interactions.
6+
7+
## Features
8+
9+
### ⌨️ Keyboard Shortcuts
10+
11+
- **Tools**: Single-key shortcuts (P, E, R, C, L, A) for quick tool switching
12+
- **Edit**: Ctrl+Z/Ctrl+Shift+Z for undo/redo
13+
- **Canvas**: Ctrl+R refresh, Ctrl+Shift+K clear
14+
- **Commands**: Ctrl+K command palette, Ctrl+/ shortcuts help
15+
16+
### 🎯 Command Palette (Ctrl+K)
17+
18+
VS Code-style quick access to all commands:
19+
- Fuzzy search with keyword matching
20+
- Keyboard navigation (↑↓ arrows, Enter to select)
21+
- Recent commands tracking
22+
- Category grouping
23+
- Shortcut display for each command
24+
25+
### 📖 Keyboard Shortcuts Help (Ctrl+/)
26+
27+
Comprehensive shortcut reference:
28+
- Organized by category (Tools, Edit, Canvas, Commands)
29+
- Tab navigation between categories
30+
- Search/filter functionality
31+
- Platform-specific display (⌘ for Mac, Ctrl for Windows/Linux)
32+
33+
## Usage
34+
35+
### Opening Dialogs
36+
37+
```javascript
38+
// Command Palette
39+
Press: Ctrl+K (Cmd+K on Mac)
40+
41+
// Shortcuts Help
42+
Press: Ctrl+/ (Cmd+/ on Mac)
43+
44+
// Cancel/Close
45+
Press: Escape
46+
```
47+
48+
### Available Shortcuts
49+
50+
#### Tools (No modifiers)
51+
- `P` - Pen tool
52+
- `E` - Eraser
53+
- `R` - Rectangle
54+
- `C` - Circle
55+
- `L` - Line
56+
- `A` - Arrow
57+
58+
#### Edit Operations
59+
- `Ctrl+Z` - Undo
60+
- `Ctrl+Shift+Z` - Redo
61+
62+
#### Canvas Operations
63+
- `Ctrl+R` - Refresh canvas
64+
- `Ctrl+Shift+K` - Clear canvas
65+
- `Ctrl+,` - Canvas settings (if available)
66+
67+
#### Commands
68+
- `Ctrl+K` - Command palette
69+
- `Ctrl+/` - Keyboard shortcuts help
70+
- `Escape` - Cancel current action
71+
72+
## Architecture
73+
74+
### Core Services
75+
76+
#### KeyboardShortcutManager (`frontend/src/services/KeyboardShortcuts.js`)
77+
78+
Manages registration and execution of keyboard shortcuts:
79+
80+
```javascript
81+
import { KeyboardShortcutManager } from '../services/KeyboardShortcuts';
82+
83+
const manager = new KeyboardShortcutManager();
84+
85+
// Register a shortcut
86+
manager.register(
87+
'k', // key
88+
{ ctrl: true }, // modifiers
89+
() => openCommandPalette(), // action
90+
'Open Command Palette', // description
91+
'Commands' // category
92+
);
93+
94+
// Handle key events
95+
document.addEventListener('keydown', (e) => manager.handleKeyDown(e));
96+
```
97+
98+
Features:
99+
- Conflict detection and warnings
100+
- Input field detection (shortcuts disabled in text inputs)
101+
- Platform-aware display (⌘/Ctrl)
102+
- Enable/disable shortcuts dynamically
103+
- Category grouping
104+
105+
#### CommandRegistry (`frontend/src/services/CommandRegistry.js`)
106+
107+
Central registry for all executable commands:
108+
109+
```javascript
110+
import { commandRegistry } from '../services/CommandRegistry';
111+
112+
// Register a command
113+
commandRegistry.register({
114+
id: 'canvas.clear',
115+
label: 'Clear Canvas',
116+
description: 'Remove all strokes from canvas',
117+
keywords: ['delete', 'erase', 'reset'],
118+
action: () => clearCanvas(),
119+
category: 'Canvas',
120+
shortcut: { key: 'k', modifiers: { ctrl: true, shift: true } },
121+
enabled: () => editingEnabled // Optional condition
122+
});
123+
124+
// Execute command
125+
await commandRegistry.execute('canvas.clear');
126+
127+
// Search commands
128+
const results = commandRegistry.search('clear');
129+
```
130+
131+
Features:
132+
- Command search with keyword matching
133+
- Enabled/visible state functions
134+
- Event listeners for execution tracking
135+
- Category organization
136+
- Batch registration
137+
138+
### Components
139+
140+
#### CommandPalette (`frontend/src/components/CommandPalette.jsx`)
141+
142+
VS Code-style command search and execution:
143+
144+
```jsx
145+
<CommandPalette
146+
open={commandPaletteOpen}
147+
onClose={() => setCommandPaletteOpen(false)}
148+
commands={commandRegistry.getAll()}
149+
onExecute={(command) => command.action()}
150+
/>
151+
```
152+
153+
Features:
154+
- Fuzzy search with keyword matching
155+
- Keyboard navigation (↑↓, Enter, Escape)
156+
- Recent commands tracking (localStorage)
157+
- Category headers
158+
- Shortcut display
159+
- Empty state messaging
160+
161+
#### KeyboardShortcutsHelp (`frontend/src/components/KeyboardShortcutsHelp.jsx`)
162+
163+
Comprehensive shortcuts reference:
164+
165+
```jsx
166+
<KeyboardShortcutsHelp
167+
open={shortcutsHelpOpen}
168+
onClose={() => setShortcutsHelpOpen(false)}
169+
shortcuts={manager.getAllShortcuts()}
170+
/>
171+
```
172+
173+
Features:
174+
- Tab navigation by category
175+
- Search/filter shortcuts
176+
- Platform-specific key display
177+
- Quick tips section
178+
- Responsive design
179+
180+
### Configuration
181+
182+
#### Default Shortcuts (`frontend/src/config/shortcuts.js`)
183+
184+
Centralized shortcut definitions:
185+
186+
```javascript
187+
export const DEFAULT_SHORTCUTS = [
188+
{
189+
id: 'tool.pen',
190+
key: 'p',
191+
modifiers: {},
192+
label: 'Select Pen Tool',
193+
description: 'Switch to pen/brush tool for drawing',
194+
category: 'Tools',
195+
keywords: ['draw', 'brush', 'pencil']
196+
},
197+
// ... more shortcuts
198+
];
199+
```
200+
201+
## Integration
202+
203+
### Canvas Component
204+
205+
The Canvas component registers all shortcuts on mount:
206+
207+
```javascript
208+
// In Canvas.js
209+
useEffect(() => {
210+
const manager = new KeyboardShortcutManager();
211+
212+
// Register commands
213+
commandRegistry.register({
214+
id: 'edit.undo',
215+
label: 'Undo',
216+
action: undo,
217+
shortcut: { key: 'z', modifiers: { ctrl: true } }
218+
});
219+
220+
// Register keyboard shortcuts
221+
manager.register('z', { ctrl: true }, undo, 'Undo', 'Edit');
222+
223+
// Add global listener
224+
const handleKeyDown = (e) => manager.handleKeyDown(e);
225+
document.addEventListener('keydown', handleKeyDown);
226+
227+
return () => {
228+
document.removeEventListener('keydown', handleKeyDown);
229+
manager.clear();
230+
};
231+
}, [dependencies]);
232+
```
233+
234+
### Adding New Shortcuts
235+
236+
1. **Define the action function** in your component
237+
2. **Add command to registry** in the useEffect hook:
238+
239+
```javascript
240+
commandRegistry.register({
241+
id: 'feature.myAction',
242+
label: 'My Action',
243+
description: 'Description of what it does',
244+
keywords: ['search', 'terms'],
245+
action: myActionFunction,
246+
category: 'Feature',
247+
shortcut: { key: 'm', modifiers: { ctrl: true } },
248+
enabled: () => someCondition // Optional
249+
});
250+
```
251+
252+
3. **Register keyboard shortcut**:
253+
254+
```javascript
255+
manager.register(
256+
'm',
257+
{ ctrl: true },
258+
myActionFunction,
259+
'My Action',
260+
'Feature'
261+
);
262+
```
263+
264+
## Best Practices
265+
266+
### 1. Use Descriptive Labels
267+
```javascript
268+
// Good
269+
label: 'Clear Canvas'
270+
description: 'Remove all strokes from canvas'
271+
272+
// Bad
273+
label: 'Clear'
274+
description: 'Clears stuff'
275+
```
276+
277+
### 2. Add Keywords for Searchability
278+
```javascript
279+
keywords: ['delete', 'erase', 'reset', 'remove'] // Good
280+
keywords: [] // Bad
281+
```
282+
283+
### 3. Check Conditions Before Execution
284+
```javascript
285+
action: () => {
286+
if (!editingEnabled) {
287+
showSnackbar('Action disabled in view-only mode');
288+
return;
289+
}
290+
performAction();
291+
}
292+
```
293+
294+
### 4. Provide Feedback
295+
```javascript
296+
action: () => {
297+
setDrawMode('pen');
298+
showSnackbar('Pen tool selected'); // User feedback
299+
}
300+
```
301+
302+
### 5. Avoid Input Field Conflicts
303+
The KeyboardShortcutManager automatically disables shortcuts when typing in input fields. For special cases:
304+
305+
```javascript
306+
manager.register(
307+
'enter',
308+
{},
309+
submitForm,
310+
'Submit Form',
311+
'Actions',
312+
false // allowInInput = false (default)
313+
);
314+
```
315+
316+
## Testing
317+
318+
### Manual Testing Checklist
319+
320+
- [ ] Ctrl+K opens command palette
321+
- [ ] Ctrl+/ opens shortcuts help
322+
- [ ] Escape closes dialogs
323+
- [ ] Tool shortcuts (P, E, R, C, L, A) switch tools
324+
- [ ] Ctrl+Z / Ctrl+Shift+Z perform undo/redo
325+
- [ ] Ctrl+R refreshes canvas
326+
- [ ] Shortcuts disabled when typing in text fields
327+
- [ ] Command palette search works
328+
- [ ] Recent commands are tracked
329+
- [ ] Arrow keys navigate command palette
330+
- [ ] Enter executes selected command
331+
332+
### Unit Tests
333+
334+
```javascript
335+
// Example test for KeyboardShortcutManager
336+
import { KeyboardShortcutManager } from '../services/KeyboardShortcuts';
337+
338+
test('registers and executes shortcut', () => {
339+
const manager = new KeyboardShortcutManager();
340+
const mockAction = jest.fn();
341+
342+
manager.register('k', { ctrl: true }, mockAction);
343+
344+
const event = new KeyboardEvent('keydown', {
345+
key: 'k',
346+
ctrlKey: true
347+
});
348+
349+
manager.handleKeyDown(event);
350+
351+
expect(mockAction).toHaveBeenCalled();
352+
});
353+
```
354+
355+
## Troubleshooting
356+
357+
### Shortcuts Not Working
358+
359+
1. **Check console for conflicts**: The manager logs conflicts when registering duplicate shortcuts
360+
2. **Verify enabledcondition**: Commands with `enabled: () => false` won't execute
361+
3. **Check input focus**: Shortcuts are disabled in text inputs by default
362+
4. **Inspect event listeners**: Ensure the global keydown listener is attached
363+
364+
### Command Palette Empty
365+
366+
1. **Verify command registration**: Check `commandRegistry.getAll()` in console
367+
2. **Check visible conditions**: Commands with `visible: () => false` won't appear
368+
3. **Clear browser cache**: Recent commands stored in localStorage may cause issues
369+
370+
### Platform-Specific Issues
371+
372+
- **Mac**: Uses `` (Command) key instead of Ctrl
373+
- **Detection**: Based on `navigator.platform.toUpperCase().indexOf('MAC')`
374+
- **Both supported**: `event.ctrlKey || event.metaKey` handles both
375+
376+
## Future Enhancements
377+
378+
- [ ] User-customizable shortcut mappings
379+
- [ ] Macro recording (repeat action sequences)
380+
- [ ] Vim-mode keybindings
381+
- [ ] Quick command history (Ctrl+P style)
382+
- [ ] Contextual command suggestions
383+
- [ ] Shortcut conflict resolution UI
384+
- [ ] Export/import shortcut configurations
385+
- [ ] Workspace-specific shortcuts
386+
387+
## Resources
388+
389+
- **VS Code Shortcuts**: Inspiration for command palette UX
390+
- **Figma Shortcuts**: Reference for design tool workflows
391+
- **Material-UI**: Component library used for dialogs
392+
- **Web Keyboard API**: MDN documentation for key events

0 commit comments

Comments
 (0)