mirror of
https://github.com/jlengrand/Maestro.git
synced 2026-03-10 08:31:19 +00:00
MAESTRO: Complete refactoring audit Task 71 - remove dead shortcutMatcher.ts
Removed completely unused shortcutMatcher.ts utility file (105 lines) and its test file (436 lines). All 3 exports (matchShortcut, isAltMetaShortcut, isAltMetaNumberShortcut) were dead code - never imported by any application code. The identical logic exists and is actively used in the useKeyboardShortcutHelpers hook.
This commit is contained in:
@@ -52,7 +52,7 @@ Download the latest release for your platform from the [Releases](https://github
|
||||
- 💬 **[Group Chat](#group-chat)** - Coordinate multiple AI agents in a single conversation. A moderator AI orchestrates discussions, routing questions to the right agents and synthesizing their responses for cross-project questions and architecture discussions.
|
||||
- 🌐 **[Mobile Remote Control](#remote-access)** - Built-in web server with QR code access. Monitor and control all your agents from your phone. Supports local network access and remote tunneling via Cloudflare for access from anywhere.
|
||||
- 💻 **[Command Line Interface](#command-line-interface)** - Full CLI (`maestro-cli`) for headless operation. List agents/groups, run playbooks from cron jobs or CI/CD pipelines, with human-readable or JSONL output for scripting.
|
||||
- 🚀 **Multi-Instance Management** - Run unlimited Claude Code instances and terminal sessions in parallel. Each agent has its own workspace, conversation history, and isolated context.
|
||||
- 🚀 **Multi-Instance Management** - Run unlimited agentss and terminal sessions in parallel. Each agent has its own workspace, conversation history, and isolated context.
|
||||
- 📬 **Message Queueing** - Queue messages while AI is busy; they're sent automatically when the agent becomes ready. Never lose a thought.
|
||||
|
||||
### Core Features
|
||||
|
||||
@@ -1,435 +0,0 @@
|
||||
/**
|
||||
* Tests for shortcutMatcher.ts
|
||||
*
|
||||
* These tests verify that keyboard shortcuts are correctly matched,
|
||||
* particularly for macOS where Alt key produces special characters.
|
||||
*
|
||||
* On macOS, pressing Alt+<key> produces special characters in e.key:
|
||||
* - Alt+P = π
|
||||
* - Alt+L = ¬
|
||||
* - Alt+T = †
|
||||
* - Alt+1 = ¡
|
||||
* - Alt+2 = ™
|
||||
* etc.
|
||||
*
|
||||
* The shortcut matcher must use e.code to detect the physical key pressed.
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
matchShortcut,
|
||||
isAltMetaShortcut,
|
||||
isAltMetaNumberShortcut,
|
||||
} from '../../../renderer/utils/shortcutMatcher';
|
||||
import type { Shortcut } from '../../../renderer/types';
|
||||
|
||||
/**
|
||||
* Helper to create a mock KeyboardEvent
|
||||
*/
|
||||
function createKeyboardEvent(options: {
|
||||
key: string;
|
||||
code?: string;
|
||||
metaKey?: boolean;
|
||||
ctrlKey?: boolean;
|
||||
altKey?: boolean;
|
||||
shiftKey?: boolean;
|
||||
}): KeyboardEvent {
|
||||
return {
|
||||
key: options.key,
|
||||
code: options.code || `Key${options.key.toUpperCase()}`,
|
||||
metaKey: options.metaKey || false,
|
||||
ctrlKey: options.ctrlKey || false,
|
||||
altKey: options.altKey || false,
|
||||
shiftKey: options.shiftKey || false,
|
||||
preventDefault: () => {},
|
||||
stopPropagation: () => {},
|
||||
} as KeyboardEvent;
|
||||
}
|
||||
|
||||
describe('shortcutMatcher', () => {
|
||||
describe('matchShortcut', () => {
|
||||
describe('basic shortcuts without Alt', () => {
|
||||
it('matches Cmd+K', () => {
|
||||
const shortcut: Shortcut = { id: 'test', label: 'Test', keys: ['Meta', 'k'] };
|
||||
const event = createKeyboardEvent({ key: 'k', code: 'KeyK', metaKey: true });
|
||||
expect(matchShortcut(event, shortcut)).toBe(true);
|
||||
});
|
||||
|
||||
it('matches Cmd+Shift+N', () => {
|
||||
const shortcut: Shortcut = { id: 'test', label: 'Test', keys: ['Meta', 'Shift', 'n'] };
|
||||
const event = createKeyboardEvent({ key: 'N', code: 'KeyN', metaKey: true, shiftKey: true });
|
||||
expect(matchShortcut(event, shortcut)).toBe(true);
|
||||
});
|
||||
|
||||
it('does not match when modifier is missing', () => {
|
||||
const shortcut: Shortcut = { id: 'test', label: 'Test', keys: ['Meta', 'k'] };
|
||||
const event = createKeyboardEvent({ key: 'k', code: 'KeyK', metaKey: false });
|
||||
expect(matchShortcut(event, shortcut)).toBe(false);
|
||||
});
|
||||
|
||||
it('does not match wrong key', () => {
|
||||
const shortcut: Shortcut = { id: 'test', label: 'Test', keys: ['Meta', 'k'] };
|
||||
const event = createKeyboardEvent({ key: 'j', code: 'KeyJ', metaKey: true });
|
||||
expect(matchShortcut(event, shortcut)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('macOS Alt+Meta shortcuts (special character handling)', () => {
|
||||
it('matches Alt+Meta+P when e.key is π (macOS)', () => {
|
||||
// On macOS, Alt+P produces π in e.key
|
||||
const shortcut: Shortcut = { id: 'processMonitor', label: 'Process Monitor', keys: ['Alt', 'Meta', 'p'] };
|
||||
const event = createKeyboardEvent({
|
||||
key: 'π', // macOS Alt+P produces this
|
||||
code: 'KeyP',
|
||||
altKey: true,
|
||||
metaKey: true,
|
||||
});
|
||||
expect(matchShortcut(event, shortcut)).toBe(true);
|
||||
});
|
||||
|
||||
it('matches Alt+Meta+L when e.key is ¬ (macOS)', () => {
|
||||
// On macOS, Alt+L produces ¬ in e.key
|
||||
const shortcut: Shortcut = { id: 'systemLogs', label: 'System Logs', keys: ['Alt', 'Meta', 'l'] };
|
||||
const event = createKeyboardEvent({
|
||||
key: '¬', // macOS Alt+L produces this
|
||||
code: 'KeyL',
|
||||
altKey: true,
|
||||
metaKey: true,
|
||||
});
|
||||
expect(matchShortcut(event, shortcut)).toBe(true);
|
||||
});
|
||||
|
||||
it('matches Alt+Meta+T when e.key is † (macOS)', () => {
|
||||
// On macOS, Alt+T produces † in e.key
|
||||
const shortcut: Shortcut = { id: 'tabSwitcher', label: 'Tab Switcher', keys: ['Alt', 'Meta', 't'] };
|
||||
const event = createKeyboardEvent({
|
||||
key: '†', // macOS Alt+T produces this
|
||||
code: 'KeyT',
|
||||
altKey: true,
|
||||
metaKey: true,
|
||||
});
|
||||
expect(matchShortcut(event, shortcut)).toBe(true);
|
||||
});
|
||||
|
||||
it('still matches Alt+Meta+P with normal e.key (non-macOS or different keyboard)', () => {
|
||||
const shortcut: Shortcut = { id: 'processMonitor', label: 'Process Monitor', keys: ['Alt', 'Meta', 'p'] };
|
||||
const event = createKeyboardEvent({
|
||||
key: 'p',
|
||||
code: 'KeyP',
|
||||
altKey: true,
|
||||
metaKey: true,
|
||||
});
|
||||
expect(matchShortcut(event, shortcut)).toBe(true);
|
||||
});
|
||||
|
||||
it('does not match Alt+Meta+P when wrong key is pressed', () => {
|
||||
const shortcut: Shortcut = { id: 'processMonitor', label: 'Process Monitor', keys: ['Alt', 'Meta', 'p'] };
|
||||
const event = createKeyboardEvent({
|
||||
key: '¬', // This is Alt+L, not Alt+P
|
||||
code: 'KeyL',
|
||||
altKey: true,
|
||||
metaKey: true,
|
||||
});
|
||||
expect(matchShortcut(event, shortcut)).toBe(false);
|
||||
});
|
||||
|
||||
it('does not match without Alt modifier', () => {
|
||||
const shortcut: Shortcut = { id: 'processMonitor', label: 'Process Monitor', keys: ['Alt', 'Meta', 'p'] };
|
||||
const event = createKeyboardEvent({
|
||||
key: 'p',
|
||||
code: 'KeyP',
|
||||
altKey: false,
|
||||
metaKey: true,
|
||||
});
|
||||
expect(matchShortcut(event, shortcut)).toBe(false);
|
||||
});
|
||||
|
||||
it('does not match without Meta modifier', () => {
|
||||
const shortcut: Shortcut = { id: 'processMonitor', label: 'Process Monitor', keys: ['Alt', 'Meta', 'p'] };
|
||||
const event = createKeyboardEvent({
|
||||
key: 'π',
|
||||
code: 'KeyP',
|
||||
altKey: true,
|
||||
metaKey: false,
|
||||
});
|
||||
expect(matchShortcut(event, shortcut)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Alt+Meta+Number shortcuts (session jump)', () => {
|
||||
it('matches Alt+Meta+1 when e.key is ¡ (macOS)', () => {
|
||||
// On macOS, Alt+1 produces ¡ in e.key
|
||||
const shortcut: Shortcut = { id: 'jump1', label: 'Jump to 1', keys: ['Alt', 'Meta', '1'] };
|
||||
const event = createKeyboardEvent({
|
||||
key: '¡', // macOS Alt+1 produces this
|
||||
code: 'Digit1',
|
||||
altKey: true,
|
||||
metaKey: true,
|
||||
});
|
||||
expect(matchShortcut(event, shortcut)).toBe(true);
|
||||
});
|
||||
|
||||
it('matches Alt+Meta+2 when e.key is ™ (macOS)', () => {
|
||||
// On macOS, Alt+2 produces ™ in e.key
|
||||
const shortcut: Shortcut = { id: 'jump2', label: 'Jump to 2', keys: ['Alt', 'Meta', '2'] };
|
||||
const event = createKeyboardEvent({
|
||||
key: '™', // macOS Alt+2 produces this
|
||||
code: 'Digit2',
|
||||
altKey: true,
|
||||
metaKey: true,
|
||||
});
|
||||
expect(matchShortcut(event, shortcut)).toBe(true);
|
||||
});
|
||||
|
||||
it('matches Alt+Meta+0 when e.key is º (macOS)', () => {
|
||||
// On macOS, Alt+0 produces º in e.key
|
||||
const shortcut: Shortcut = { id: 'jump0', label: 'Jump to 10', keys: ['Alt', 'Meta', '0'] };
|
||||
const event = createKeyboardEvent({
|
||||
key: 'º', // macOS Alt+0 produces this
|
||||
code: 'Digit0',
|
||||
altKey: true,
|
||||
metaKey: true,
|
||||
});
|
||||
expect(matchShortcut(event, shortcut)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('arrow key shortcuts', () => {
|
||||
it('matches Alt+Meta+ArrowLeft', () => {
|
||||
const shortcut: Shortcut = { id: 'toggleSidebar', label: 'Toggle Sidebar', keys: ['Alt', 'Meta', 'ArrowLeft'] };
|
||||
const event = createKeyboardEvent({
|
||||
key: 'ArrowLeft',
|
||||
code: 'ArrowLeft',
|
||||
altKey: true,
|
||||
metaKey: true,
|
||||
});
|
||||
expect(matchShortcut(event, shortcut)).toBe(true);
|
||||
});
|
||||
|
||||
it('matches Alt+Meta+ArrowRight', () => {
|
||||
const shortcut: Shortcut = { id: 'toggleRightPanel', label: 'Toggle Right Panel', keys: ['Alt', 'Meta', 'ArrowRight'] };
|
||||
const event = createKeyboardEvent({
|
||||
key: 'ArrowRight',
|
||||
code: 'ArrowRight',
|
||||
altKey: true,
|
||||
metaKey: true,
|
||||
});
|
||||
expect(matchShortcut(event, shortcut)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Shift+bracket shortcuts', () => {
|
||||
it('matches Cmd+Shift+[ when e.key is {', () => {
|
||||
const shortcut: Shortcut = { id: 'prevTab', label: 'Previous Tab', keys: ['Meta', 'Shift', '['] };
|
||||
const event = createKeyboardEvent({
|
||||
key: '{', // Shift+[ produces { on US keyboard
|
||||
code: 'BracketLeft',
|
||||
metaKey: true,
|
||||
shiftKey: true,
|
||||
});
|
||||
expect(matchShortcut(event, shortcut)).toBe(true);
|
||||
});
|
||||
|
||||
it('matches Cmd+Shift+] when e.key is }', () => {
|
||||
const shortcut: Shortcut = { id: 'nextTab', label: 'Next Tab', keys: ['Meta', 'Shift', ']'] };
|
||||
const event = createKeyboardEvent({
|
||||
key: '}', // Shift+] produces } on US keyboard
|
||||
code: 'BracketRight',
|
||||
metaKey: true,
|
||||
shiftKey: true,
|
||||
});
|
||||
expect(matchShortcut(event, shortcut)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Shift+number shortcuts', () => {
|
||||
it('matches Cmd+Shift+1 when e.key is !', () => {
|
||||
const shortcut: Shortcut = { id: 'goToAutoRun', label: 'Go to Auto Run', keys: ['Meta', 'Shift', '1'] };
|
||||
const event = createKeyboardEvent({
|
||||
key: '!', // Shift+1 produces ! on US keyboard
|
||||
code: 'Digit1',
|
||||
metaKey: true,
|
||||
shiftKey: true,
|
||||
});
|
||||
expect(matchShortcut(event, shortcut)).toBe(true);
|
||||
});
|
||||
|
||||
it('matches Cmd+Shift+2 when e.key is @', () => {
|
||||
const shortcut: Shortcut = { id: 'test', label: 'Test', keys: ['Meta', 'Shift', '2'] };
|
||||
const event = createKeyboardEvent({
|
||||
key: '@', // Shift+2 produces @ on US keyboard
|
||||
code: 'Digit2',
|
||||
metaKey: true,
|
||||
shiftKey: true,
|
||||
});
|
||||
expect(matchShortcut(event, shortcut)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAltMetaShortcut', () => {
|
||||
it('returns true for Alt+Meta+L with allowed keys [l, p]', () => {
|
||||
const event = createKeyboardEvent({
|
||||
key: '¬', // macOS Alt+L
|
||||
code: 'KeyL',
|
||||
altKey: true,
|
||||
metaKey: true,
|
||||
});
|
||||
expect(isAltMetaShortcut(event, ['l', 'p'])).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for Alt+Meta+P with allowed keys [l, p]', () => {
|
||||
const event = createKeyboardEvent({
|
||||
key: 'π', // macOS Alt+P
|
||||
code: 'KeyP',
|
||||
altKey: true,
|
||||
metaKey: true,
|
||||
});
|
||||
expect(isAltMetaShortcut(event, ['l', 'p'])).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false for Alt+Meta+T when not in allowed keys', () => {
|
||||
const event = createKeyboardEvent({
|
||||
key: '†', // macOS Alt+T
|
||||
code: 'KeyT',
|
||||
altKey: true,
|
||||
metaKey: true,
|
||||
});
|
||||
expect(isAltMetaShortcut(event, ['l', 'p'])).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false without Alt modifier', () => {
|
||||
const event = createKeyboardEvent({
|
||||
key: 'p',
|
||||
code: 'KeyP',
|
||||
altKey: false,
|
||||
metaKey: true,
|
||||
});
|
||||
expect(isAltMetaShortcut(event, ['l', 'p'])).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false without Meta modifier', () => {
|
||||
const event = createKeyboardEvent({
|
||||
key: 'π',
|
||||
code: 'KeyP',
|
||||
altKey: true,
|
||||
metaKey: false,
|
||||
});
|
||||
expect(isAltMetaShortcut(event, ['l', 'p'])).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAltMetaNumberShortcut', () => {
|
||||
it('returns true for Alt+Meta+1 (macOS e.key = ¡)', () => {
|
||||
const event = createKeyboardEvent({
|
||||
key: '¡', // macOS Alt+1
|
||||
code: 'Digit1',
|
||||
altKey: true,
|
||||
metaKey: true,
|
||||
});
|
||||
expect(isAltMetaNumberShortcut(event)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for Alt+Meta+5', () => {
|
||||
const event = createKeyboardEvent({
|
||||
key: '∞', // macOS Alt+5
|
||||
code: 'Digit5',
|
||||
altKey: true,
|
||||
metaKey: true,
|
||||
});
|
||||
expect(isAltMetaNumberShortcut(event)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for Alt+Meta+0', () => {
|
||||
const event = createKeyboardEvent({
|
||||
key: 'º', // macOS Alt+0
|
||||
code: 'Digit0',
|
||||
altKey: true,
|
||||
metaKey: true,
|
||||
});
|
||||
expect(isAltMetaNumberShortcut(event)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false for Alt+Meta+Letter', () => {
|
||||
const event = createKeyboardEvent({
|
||||
key: 'π', // macOS Alt+P
|
||||
code: 'KeyP',
|
||||
altKey: true,
|
||||
metaKey: true,
|
||||
});
|
||||
expect(isAltMetaNumberShortcut(event)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false without Alt modifier', () => {
|
||||
const event = createKeyboardEvent({
|
||||
key: '1',
|
||||
code: 'Digit1',
|
||||
altKey: false,
|
||||
metaKey: true,
|
||||
});
|
||||
expect(isAltMetaNumberShortcut(event)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false without Meta modifier', () => {
|
||||
const event = createKeyboardEvent({
|
||||
key: '¡',
|
||||
code: 'Digit1',
|
||||
altKey: true,
|
||||
metaKey: false,
|
||||
});
|
||||
expect(isAltMetaNumberShortcut(event)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('handles missing e.code gracefully', () => {
|
||||
const shortcut: Shortcut = { id: 'test', label: 'Test', keys: ['Alt', 'Meta', 'p'] };
|
||||
const event = {
|
||||
key: 'π',
|
||||
code: undefined,
|
||||
metaKey: true,
|
||||
altKey: true,
|
||||
shiftKey: false,
|
||||
ctrlKey: false,
|
||||
} as unknown as KeyboardEvent;
|
||||
// Should fall back to e.key comparison (won't match 'π' to 'p')
|
||||
expect(matchShortcut(event, shortcut)).toBe(false);
|
||||
});
|
||||
|
||||
it('handles empty e.code string', () => {
|
||||
const shortcut: Shortcut = { id: 'test', label: 'Test', keys: ['Alt', 'Meta', 'p'] };
|
||||
const event = {
|
||||
key: 'π',
|
||||
code: '',
|
||||
metaKey: true,
|
||||
altKey: true,
|
||||
shiftKey: false,
|
||||
ctrlKey: false,
|
||||
} as unknown as KeyboardEvent;
|
||||
// Should fall back to e.key comparison (won't match 'π' to 'p')
|
||||
expect(matchShortcut(event, shortcut)).toBe(false);
|
||||
});
|
||||
|
||||
it('treats Ctrl as equivalent to Meta', () => {
|
||||
const shortcut: Shortcut = { id: 'test', label: 'Test', keys: ['Meta', 'k'] };
|
||||
const event = createKeyboardEvent({
|
||||
key: 'k',
|
||||
code: 'KeyK',
|
||||
ctrlKey: true,
|
||||
metaKey: false,
|
||||
});
|
||||
expect(matchShortcut(event, shortcut)).toBe(true);
|
||||
});
|
||||
|
||||
it('matches shortcut with Ctrl in config using metaKey', () => {
|
||||
const shortcut: Shortcut = { id: 'test', label: 'Test', keys: ['Ctrl', 'k'] };
|
||||
const event = createKeyboardEvent({
|
||||
key: 'k',
|
||||
code: 'KeyK',
|
||||
metaKey: true,
|
||||
ctrlKey: false,
|
||||
});
|
||||
expect(matchShortcut(event, shortcut)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,104 +0,0 @@
|
||||
/**
|
||||
* Utility for matching keyboard events against shortcut configurations.
|
||||
*
|
||||
* Handles platform-specific quirks like macOS Alt key producing special characters
|
||||
* in e.key (e.g., Alt+P = π, Alt+L = ¬) by falling back to e.code.
|
||||
*/
|
||||
|
||||
import type { Shortcut } from '../types';
|
||||
|
||||
/**
|
||||
* Check if a keyboard event matches a shortcut configuration.
|
||||
*
|
||||
* @param e - The keyboard event to check
|
||||
* @param shortcut - The shortcut configuration to match against
|
||||
* @returns true if the event matches the shortcut
|
||||
*
|
||||
* @example
|
||||
* // Shortcut config: { keys: ['Alt', 'Meta', 'p'] }
|
||||
* // On macOS, Alt+Meta+P produces e.key = 'π', but e.code = 'KeyP'
|
||||
* matchShortcut(event, shortcut) // Returns true when Alt+Meta+P is pressed
|
||||
*/
|
||||
export function matchShortcut(e: KeyboardEvent, shortcut: Shortcut): boolean {
|
||||
const keys = shortcut.keys.map(k => k.toLowerCase());
|
||||
|
||||
const metaPressed = e.metaKey || e.ctrlKey;
|
||||
const shiftPressed = e.shiftKey;
|
||||
const altPressed = e.altKey;
|
||||
const key = e.key.toLowerCase();
|
||||
|
||||
const configMeta = keys.includes('meta') || keys.includes('ctrl') || keys.includes('command');
|
||||
const configShift = keys.includes('shift');
|
||||
const configAlt = keys.includes('alt');
|
||||
|
||||
// Check modifier keys match exactly
|
||||
if (metaPressed !== configMeta) return false;
|
||||
if (shiftPressed !== configShift) return false;
|
||||
if (altPressed !== configAlt) return false;
|
||||
|
||||
const mainKey = keys[keys.length - 1];
|
||||
|
||||
// Direct key matches for special keys
|
||||
if (mainKey === '/' && key === '/') return true;
|
||||
if (mainKey === 'arrowleft' && key === 'arrowleft') return true;
|
||||
if (mainKey === 'arrowright' && key === 'arrowright') return true;
|
||||
if (mainKey === 'arrowup' && key === 'arrowup') return true;
|
||||
if (mainKey === 'arrowdown' && key === 'arrowdown') return true;
|
||||
if (mainKey === 'backspace' && key === 'backspace') return true;
|
||||
|
||||
// Handle Shift+[ producing { and Shift+] producing }
|
||||
if (mainKey === '[' && (key === '[' || key === '{')) return true;
|
||||
if (mainKey === ']' && (key === ']' || key === '}')) return true;
|
||||
|
||||
// Handle Shift+number producing symbol (US keyboard layout)
|
||||
// Shift+1='!', Shift+2='@', Shift+3='#', etc.
|
||||
const shiftNumberMap: Record<string, string> = {
|
||||
'!': '1', '@': '2', '#': '3', '$': '4', '%': '5',
|
||||
'^': '6', '&': '7', '*': '8', '(': '9', ')': '0'
|
||||
};
|
||||
if (shiftNumberMap[key] === mainKey) return true;
|
||||
|
||||
// For Alt+Meta shortcuts on macOS, e.key produces special characters (e.g., Alt+p = π, Alt+l = ¬)
|
||||
// Use e.code to get the physical key pressed instead
|
||||
if (altPressed && e.code) {
|
||||
const codeKey = e.code.replace('Key', '').replace('Digit', '').toLowerCase();
|
||||
return codeKey === mainKey;
|
||||
}
|
||||
|
||||
return key === mainKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a keyboard event matches the pattern for Alt+Meta+Letter shortcuts
|
||||
* when modals are open (used for system utility shortcuts that should work
|
||||
* even when modals are blocking other shortcuts).
|
||||
*
|
||||
* @param e - The keyboard event to check
|
||||
* @param allowedKeys - Array of allowed main keys (e.g., ['l', 'p'] for logs/process monitor)
|
||||
* @returns true if the event matches an allowed Alt+Meta shortcut
|
||||
*
|
||||
* @example
|
||||
* // Allow Alt+Meta+L and Alt+Meta+P even when modals are open
|
||||
* isAltMetaShortcut(event, ['l', 'p'])
|
||||
*/
|
||||
export function isAltMetaShortcut(e: KeyboardEvent, allowedKeys: string[]): boolean {
|
||||
if (!e.altKey || !(e.metaKey || e.ctrlKey)) return false;
|
||||
|
||||
// Must use e.code for Alt key combos on macOS because e.key produces special characters
|
||||
const codeKey = e.code?.replace('Key', '').replace('Digit', '').toLowerCase() || '';
|
||||
return allowedKeys.includes(codeKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a keyboard event matches Alt+Meta+Number (for session jump shortcuts).
|
||||
*
|
||||
* @param e - The keyboard event to check
|
||||
* @returns true if the event is Alt+Meta+[0-9]
|
||||
*/
|
||||
export function isAltMetaNumberShortcut(e: KeyboardEvent): boolean {
|
||||
if (!e.altKey || !(e.metaKey || e.ctrlKey)) return false;
|
||||
|
||||
// Must use e.code for Alt key combos on macOS because e.key produces special characters
|
||||
// Alt+1 produces '¡', Alt+2 produces '™', etc. instead of digits
|
||||
return /^Digit[0-9]$/.test(e.code || '');
|
||||
}
|
||||
Reference in New Issue
Block a user