import { useEffect } from 'react'; import { useEditor } from '@craftjs/core'; import { findDeletableTarget } from '../utils/craft-helpers'; function isInputFocused(): boolean { const el = document.activeElement; if (!el) return false; const tag = el.tagName.toLowerCase(); if (tag === 'input' || tag === 'textarea' || tag === 'select') return true; if ((el as HTMLElement).isContentEditable) return true; return false; } export function useKeyboardShortcuts() { const { actions, query } = useEditor((state) => { const selectedIds = state.events.selected; const selectedId = selectedIds ? Array.from(selectedIds)[0] : null; return { selectedId }; }); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { // Skip when typing in inputs if (isInputFocused()) return; const ctrl = e.ctrlKey || e.metaKey; // Ctrl+Z: Undo if (ctrl && !e.shiftKey && e.key === 'z') { e.preventDefault(); try { actions.history.undo(); } catch (err) { // No more undo steps } return; } // Ctrl+Y or Ctrl+Shift+Z: Redo if ((ctrl && e.key === 'y') || (ctrl && e.shiftKey && e.key === 'z') || (ctrl && e.shiftKey && e.key === 'Z')) { e.preventDefault(); try { actions.history.redo(); } catch (err) { // No more redo steps } return; } // Delete/Backspace: delete selected node if (e.key === 'Delete' || e.key === 'Backspace') { e.preventDefault(); try { const selected = query.getEvent('selected').all(); if (selected.length > 0) { const target = findDeletableTarget(query, selected[0]); if (target) actions.delete(target); } } catch (err) { console.error('Delete failed:', err); } return; } // Ctrl+D: duplicate selected if (ctrl && (e.key === 'd' || e.key === 'D')) { e.preventDefault(); try { const selected = query.getEvent('selected').all(); if (selected.length > 0) { const nodeId = selected[0]; if (nodeId !== 'ROOT') { const node = query.node(nodeId).get(); const parentId = node?.data?.parent; if (parentId) { const tree = query.node(nodeId).toNodeTree(); actions.addNodeTree(tree, parentId); } } } } catch (err) { console.error('Duplicate failed:', err); } return; } // Escape: deselect all if (e.key === 'Escape') { e.preventDefault(); try { actions.clearEvents(); } catch (err) { // Ignore } return; } }; document.addEventListener('keydown', handleKeyDown); return () => document.removeEventListener('keydown', handleKeyDown); }, [actions, query]); }