'use client'; import { useEffect } from 'react'; import { isContextMenuOpen } from '../components/ContextMenu'; import { isModalOpen } from '../components/Modal'; import { useGalleryStoreApi } from '../store/context'; import { mediaForView } from '../store/selectors'; /** * Wire Apple-Photos-style global keyboard shortcuts. * * `enabled` is a parameter (not a call-site condition) so hook order stays * stable: an embedding host that owns its own key handling passes `false`. */ export function useKeyboardShortcuts(enabled: boolean = true) { const api = useGalleryStoreApi(); useEffect(() => { if (!enabled) return; const onKey = (e: KeyboardEvent) => { const target = e.target as HTMLElement | null; // Never hijack typing in inputs / editable fields. if ( target && (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) ) { return; } const state = api.getState(); const selection = [...state.selection]; const meta = e.metaKey || e.ctrlKey; // Cmd/Ctrl+A — select all in the current view. if (meta && (e.key === 'a' || e.key === 'A')) { if (state.lightboxId || state.editorId) return; e.preventDefault(); const ids = mediaForView(state).map((m) => m.id); state.selectMany(ids); return; } // Delete / Backspace — trash (or permanently delete in the bin). if ((e.key === 'Delete' || e.key === 'Backspace') && selection.length) { e.preventDefault(); if (state.view === 'recently-deleted') state.deletePermanently(selection); else state.trash(selection); return; } // F — toggle favourite. if ((e.key === 'f' || e.key === 'F') && !meta && selection.length) { e.preventDefault(); state.toggleFavorite(selection); return; } // Escape — clear selection / focus, then leave full screen. if (e.key === 'Escape') { // Anything layered above the shell owns Escape first: the lightbox, // editors, the camera, modals and context menus each close themselves. const overlayOpen = Boolean(state.lightboxId) || Boolean(state.editorId) || state.cameraOpen || isModalOpen() || isContextMenuOpen(); if (overlayOpen) return; if (state.objectFocus) state.setObjectFocus(null); else if (state.selection.size) state.clearSelection(); else if (state.fullscreen) state.setFullscreen(false); } // Enter / Space — open the (single) selection in the lightbox. if ((e.key === 'Enter' || e.key === ' ') && selection.length === 1 && !state.lightboxId) { e.preventDefault(); state.openLightbox(selection[0]!); } }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, [api, enabled]); }