UNPKG

qnce-engine

Version:

Core QNCE (Quantum Narrative Convergence Engine) - Framework agnostic narrative engine with performance optimization

162 lines 6.43 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useKeyboardShortcuts = useKeyboardShortcuts; const react_1 = require("react"); /** * useKeyboardShortcuts Hook * * Provides keyboard shortcuts for common QNCE operations like undo/redo and autosave. * * Features: * - Configurable key bindings * - Support for modifier keys (Ctrl, Alt, Shift, Meta) * - Automatic cleanup on unmount * - Prevention of default browser behavior * - Accessibility-friendly implementation * * Default shortcuts: * - Ctrl+Z / Cmd+Z: Undo * - Ctrl+Y / Cmd+Y / Ctrl+Shift+Z: Redo * - Ctrl+S / Cmd+S: Manual autosave * - Ctrl+R / Cmd+R: Reset narrative (disabled by default) */ function useKeyboardShortcuts(engine, config = {}) { const { enabled = true, bindings = { undo: ['ctrl+z', 'cmd+z'], redo: ['ctrl+y', 'cmd+y', 'ctrl+shift+z'], save: ['ctrl+s', 'cmd+s'], reset: [] // Disabled by default for safety }, preventDefault = true, target = document } = config; // Parse key combination string into components const parseKeyCombo = (0, react_1.useCallback)((combo) => { const parts = combo.toLowerCase().split('+'); const key = parts[parts.length - 1]; const modifiers = parts.slice(0, -1); return { key, ctrl: modifiers.includes('ctrl'), alt: modifiers.includes('alt'), shift: modifiers.includes('shift'), meta: modifiers.includes('cmd') || modifiers.includes('meta') }; }, []); // Check if a keyboard event matches a key combination const matchesKeyCombo = (0, react_1.useCallback)((event, combo) => { const parsed = parseKeyCombo(combo); return (event.key.toLowerCase() === parsed.key && event.ctrlKey === parsed.ctrl && event.altKey === parsed.alt && event.shiftKey === parsed.shift && event.metaKey === parsed.meta); }, [parseKeyCombo]); // Handle keyboard events const handleKeyDown = (0, react_1.useCallback)((event) => { const keyboardEvent = event; if (!enabled || !engine) return; // Skip if user is typing in an input field const activeElement = document.activeElement; if (activeElement && (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA' || activeElement.tagName === 'SELECT' || activeElement.getAttribute('contenteditable') === 'true')) { return; } let handled = false; // Check undo shortcuts for (const combo of bindings.undo || []) { if (matchesKeyCombo(keyboardEvent, combo)) { if (engine.canUndo()) { try { engine.undo(); } catch (error) { console.error('[QNCE] Undo failed:', error); } handled = true; break; } } } // Check redo shortcuts if (!handled) { for (const combo of bindings.redo || []) { if (matchesKeyCombo(keyboardEvent, combo)) { if (engine.canRedo()) { try { engine.redo(); } catch (error) { console.error('[QNCE] Redo failed:', error); } handled = true; break; } } } } // Check autosave shortcuts if (!handled) { for (const combo of bindings.save || []) { if (matchesKeyCombo(keyboardEvent, combo)) { engine.manualAutosave().catch((error) => { console.warn('[QNCE] Manual autosave failed:', error.message); }); handled = true; break; } } } // Check reset shortcuts (if enabled) if (!handled && bindings.reset && bindings.reset.length > 0) { for (const combo of bindings.reset) { if (matchesKeyCombo(keyboardEvent, combo)) { // Add confirmation for reset to prevent accidental data loss if (window.confirm('Are you sure you want to reset the narrative? This will lose all progress.')) { try { engine.resetNarrative(); } catch (error) { console.error('[QNCE] Reset failed:', error); } handled = true; break; } } } } // Prevent default browser behavior if we handled the event if (handled && preventDefault) { keyboardEvent.preventDefault(); keyboardEvent.stopPropagation(); } }, [enabled, engine, bindings, matchesKeyCombo, preventDefault]); // Set up keyboard event listeners (0, react_1.useEffect)(() => { if (!enabled || !target) return; const currentTarget = target; currentTarget.addEventListener('keydown', handleKeyDown); return () => { currentTarget.removeEventListener('keydown', handleKeyDown); }; }, [enabled, target, handleKeyDown]); // Log active shortcuts for debugging (development only) (0, react_1.useEffect)(() => { if (!enabled || process.env.NODE_ENV === 'production') return; const shortcuts = []; if (bindings.undo?.length) shortcuts.push(`Undo: ${bindings.undo.join(', ')}`); if (bindings.redo?.length) shortcuts.push(`Redo: ${bindings.redo.join(', ')}`); if (bindings.save?.length) shortcuts.push(`Save: ${bindings.save.join(', ')}`); if (bindings.reset?.length) shortcuts.push(`Reset: ${bindings.reset.join(', ')}`); if (shortcuts.length > 0) { console.log('[QNCE] Active keyboard shortcuts:', shortcuts.join(' | ')); } }, [enabled, bindings]); } //# sourceMappingURL=useKeyboardShortcuts.js.map