Files
pastel-ui/lib/hooks/useKeyboard.ts
valknarness 638ba67189 fix: correct keyboard shortcut modifier matching logic
The previous implementation had inverted logic that prevented keyboard
shortcuts from working. The check `shortcut.ctrl ? event.ctrlKey : !event.ctrlKey`
meant that if ctrl wasn't specified, it required ctrl to NOT be pressed,
which broke shortcuts with only meta keys.

**Changes:**
- Fix modifier matching: only check modifiers that are explicitly required (=== true)
- Add check to prevent unwanted modifiers (e.g., if only meta+c, reject ctrl+meta+c)
- Improve cross-platform meta/cmd key handling (Mac uses metaKey, Windows/Linux uses ctrlKey)
- Allow meta key to permit ctrl on non-Mac platforms (meta is treated as ctrl equivalent)

**Now working shortcuts in playground:**
- Cmd/Ctrl+C: Copy color to clipboard
- Cmd/Ctrl+S: Share color (copy URL)
- Cmd/Ctrl+R: Generate random color

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 16:22:42 +01:00

107 lines
2.8 KiB
TypeScript

import { useEffect } from 'react';
export interface KeyboardShortcut {
key: string;
ctrl?: boolean;
shift?: boolean;
alt?: boolean;
meta?: boolean;
handler: (event: KeyboardEvent) => void;
description?: string;
}
/**
* Hook to register keyboard shortcuts
*
* @example
* ```tsx
* useKeyboard([
* {
* key: 'c',
* meta: true, // Cmd on Mac, Ctrl on Windows
* handler: () => copyToClipboard(),
* description: 'Copy color',
* },
* {
* key: 'k',
* meta: true,
* handler: () => openCommandPalette(),
* description: 'Open command palette',
* },
* ]);
* ```
*/
export function useKeyboard(shortcuts: KeyboardShortcut[]) {
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
for (const shortcut of shortcuts) {
const keyMatches = event.key.toLowerCase() === shortcut.key.toLowerCase();
// Check if required modifiers match (only check if explicitly required)
const ctrlMatches = shortcut.ctrl === true ? event.ctrlKey : true;
const shiftMatches = shortcut.shift === true ? event.shiftKey : true;
const altMatches = shortcut.alt === true ? event.altKey : true;
// Handle meta/cmd key with cross-platform support
let metaMatches = true;
if (shortcut.meta === true) {
// On Mac: require Cmd key
// On Windows/Linux: accept Ctrl key as Cmd equivalent
const isMac = navigator.platform.includes('Mac');
metaMatches = isMac ? event.metaKey : event.ctrlKey;
}
// Ensure unwanted modifiers are not pressed (unless explicitly required)
const noExtraCtrl = shortcut.ctrl === true || shortcut.meta === true || !event.ctrlKey;
const noExtraShift = shortcut.shift === true || !event.shiftKey;
const noExtraAlt = shortcut.alt === true || !event.altKey;
const noExtraMeta = shortcut.meta === true || !event.metaKey;
if (
keyMatches &&
ctrlMatches &&
shiftMatches &&
altMatches &&
metaMatches &&
noExtraCtrl &&
noExtraShift &&
noExtraAlt &&
noExtraMeta
) {
event.preventDefault();
shortcut.handler(event);
break;
}
}
};
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, [shortcuts]);
}
/**
* Hook to register a single keyboard shortcut (convenience wrapper)
*/
export function useKeyboardShortcut(
key: string,
handler: (event: KeyboardEvent) => void,
modifiers?: {
ctrl?: boolean;
shift?: boolean;
alt?: boolean;
meta?: boolean;
}
) {
useKeyboard([
{
key,
...modifiers,
handler,
},
]);
}