Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | /**
* @fileoverview Keyboard Shortcuts Overlay Component
* @description Shows all available keyboard shortcuts when pressing ?
* @module components/KeyboardShortcuts
*/
import { useState, useEffect } from 'react';
import './KeyboardShortcuts.css';
const SHORTCUTS = [
{ category: 'Simulation', shortcuts: [
{ keys: ['Space'], description: 'Play/Pause simulation' },
{ keys: ['R'], description: 'Reset simulation' },
{ keys: ['C'], description: 'Clear all atoms' },
]},
{ category: 'View', shortcuts: [
{ keys: ['B'], description: 'Toggle bonds' },
{ keys: ['V'], description: 'Toggle velocity vectors' },
{ keys: ['L'], description: 'Toggle atom labels' },
{ keys: ['G'], description: 'Toggle grid' },
{ keys: ['F'], description: 'Toggle fullscreen' },
]},
{ category: 'Navigation', shortcuts: [
{ keys: ['↑', '↓', '←', '→'], description: 'Move player atom' },
{ keys: ['Scroll'], description: 'Zoom in/out' },
{ keys: ['Drag'], description: 'Pan canvas' },
{ keys: ['+', '-'], description: 'Zoom in/out' },
{ keys: ['0'], description: 'Reset zoom' },
]},
{ category: 'Edit', shortcuts: [
{ keys: ['Ctrl', 'Z'], description: 'Undo' },
{ keys: ['Ctrl', 'Y'], description: 'Redo' },
{ keys: ['Ctrl', 'A'], description: 'Select all atoms' },
{ keys: ['Delete'], description: 'Delete selected atoms' },
{ keys: ['Ctrl', 'C'], description: 'Copy selected atoms' },
{ keys: ['Ctrl', 'V'], description: 'Paste atoms' },
]},
{ category: 'Tools', shortcuts: [
{ keys: ['S'], description: 'Screenshot' },
{ keys: ['?'], description: 'Show/hide shortcuts' },
{ keys: ['Esc'], description: 'Close modal/Cancel' },
]},
];
/**
* Keyboard Shortcuts Overlay Component
*/
function KeyboardShortcuts() {
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
const handleKeyDown = (e) => {
// Don't trigger if typing in an input field
const isInputField = ['INPUT', 'TEXTAREA', 'SELECT'].includes(e.target.tagName);
if (isInputField) return;
// Show on ? key (Shift + / or direct ?)
if (e.key === '?' || (e.shiftKey && e.code === 'Slash')) {
e.preventDefault();
e.stopPropagation();
setIsVisible(prev => !prev);
}
// Close on Escape
if (e.key === 'Escape' && isVisible) {
e.preventDefault();
setIsVisible(false);
}
};
// Use capture phase to ensure we get the event first
window.addEventListener('keydown', handleKeyDown, true);
return () => window.removeEventListener('keydown', handleKeyDown, true);
}, [isVisible]);
if (!isVisible) return null;
return (
<div className="shortcuts-overlay" onClick={() => setIsVisible(false)}>
<div className="shortcuts-modal" onClick={e => e.stopPropagation()}>
<div className="shortcuts-header">
<h2>⌨️ Keyboard Shortcuts</h2>
<button className="shortcuts-close" onClick={() => setIsVisible(false)}>×</button>
</div>
<div className="shortcuts-content">
{SHORTCUTS.map(category => (
<div key={category.category} className="shortcuts-category">
<h3>{category.category}</h3>
<div className="shortcuts-list">
{category.shortcuts.map((shortcut, idx) => (
<div key={idx} className="shortcut-item">
<div className="shortcut-keys">
{shortcut.keys.map((key, kidx) => (
<span key={kidx}>
<kbd>{key}</kbd>
{kidx < shortcut.keys.length - 1 && <span className="key-separator">+</span>}
</span>
))}
</div>
<span className="shortcut-desc">{shortcut.description}</span>
</div>
))}
</div>
</div>
))}
</div>
<div className="shortcuts-footer">
Press <kbd>?</kbd> to toggle this overlay
</div>
</div>
</div>
);
}
export default KeyboardShortcuts;
|