import { useEffect } from 'react'; import { useHistoryStore } from '@/store/history-store'; import { useToolStore, useLayerStore, useTextStore } from '@/store'; import { duplicateLayerWithHistory, deleteLayerWithHistory } from '@/lib/layer-operations'; import type { ToolType } from '@/types'; /** * Keyboard shortcuts configuration */ interface KeyboardShortcut { key: string; ctrl?: boolean; shift?: boolean; alt?: boolean; meta?: boolean; handler: () => void; description: string; } /** * Tool shortcuts mapping */ const TOOL_SHORTCUTS: Record = { '1': 'pencil', '2': 'brush', '3': 'eraser', '4': 'fill', '5': 'eyedropper', '6': 'text', '7': 'select', }; /** * Hook to manage keyboard shortcuts */ export function useKeyboardShortcuts() { const { undo, redo, canUndo, canRedo } = useHistoryStore(); const { setActiveTool } = useToolStore(); const { layers, activeLayerId, setActiveLayer } = useLayerStore(); const { isOnCanvasEditorActive } = useTextStore(); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { // Check if we're in an input field or text editor is active const target = e.target as HTMLElement; if ( isOnCanvasEditorActive || target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable ) { return; } // Tool selection shortcuts: 1-7 if (TOOL_SHORTCUTS[e.key]) { e.preventDefault(); setActiveTool(TOOL_SHORTCUTS[e.key]); return; } // Layer navigation: Arrow Up/Down if (e.key === 'ArrowUp' || e.key === 'ArrowDown') { e.preventDefault(); // Sort layers by order (highest first, same as UI) const sortedLayers = [...layers].sort((a, b) => b.order - a.order); const currentIndex = sortedLayers.findIndex((l) => l.id === activeLayerId); if (currentIndex === -1) return; if (e.key === 'ArrowUp' && currentIndex > 0) { setActiveLayer(sortedLayers[currentIndex - 1].id); } else if (e.key === 'ArrowDown' && currentIndex < sortedLayers.length - 1) { setActiveLayer(sortedLayers[currentIndex + 1].id); } return; } // Delete layer: Delete or Backspace (without modifier keys) if ((e.key === 'Delete' || e.key === 'Backspace') && !e.ctrlKey && !e.metaKey && !e.shiftKey) { e.preventDefault(); if (activeLayerId && layers.length > 1) { if (confirm('Delete this layer?')) { deleteLayerWithHistory(activeLayerId); } } return; } // Duplicate layer: Ctrl+D if ((e.ctrlKey || e.metaKey) && e.key === 'd' && !e.shiftKey) { e.preventDefault(); if (activeLayerId) { duplicateLayerWithHistory(activeLayerId); } return; } // Undo: Ctrl+Z (but not Ctrl+Shift+Z) if ((e.ctrlKey || e.metaKey) && e.key === 'z' && !e.shiftKey) { e.preventDefault(); if (canUndo()) { undo(); } return; } // Redo: Ctrl+Shift+Z or Ctrl+Y if ( ((e.ctrlKey || e.metaKey) && e.key === 'z' && e.shiftKey) || ((e.ctrlKey || e.metaKey) && e.key === 'y') ) { e.preventDefault(); if (canRedo()) { redo(); } return; } }; window.addEventListener('keydown', handleKeyDown); return () => { window.removeEventListener('keydown', handleKeyDown); }; }, [ undo, redo, canUndo, canRedo, setActiveTool, layers, activeLayerId, setActiveLayer, isOnCanvasEditorActive, ]); } /** * Get keyboard shortcut display string */ export function getShortcutDisplay(shortcut: { key: string; ctrl?: boolean; shift?: boolean; alt?: boolean; meta?: boolean; }): string { const parts: string[] = []; const isMac = typeof navigator !== 'undefined' && /Mac|iPhone|iPad|iPod/.test(navigator.platform); if (shortcut.ctrl || shortcut.meta) { parts.push(isMac ? '⌘' : 'Ctrl'); } if (shortcut.shift) { parts.push(isMac ? '⇧' : 'Shift'); } if (shortcut.alt) { parts.push(isMac ? '⌥' : 'Alt'); } parts.push(shortcut.key.toUpperCase()); return parts.join(isMac ? '' : '+'); }