import { useEffect, type RefObject } from "react"; import { useNavigate } from "react-router-dom"; import type { MediaParticleHandle } from "@/features/particles/media-particle-view"; import { isTypingTarget } from "@/lib/keyboard"; const SEEK_DELTA_SEC = 5; interface UseStreamNavigationKeysOptions { next: () => void; prev: () => void; currentIndex: number; childrenLength: number; mediaRef: RefObject; } /** * Arrow keys (with shift+arrow seek), Escape. Skipped while playback is * paused for any reason (overlay, hold-space, compose). */ export function useStreamNavigationKeys({ next, prev, currentIndex, childrenLength, mediaRef, }: UseStreamNavigationKeysOptions) { const navigate = useNavigate(); useEffect(() => { const onKeyDown = (e: KeyboardEvent) => { if (isTypingTarget(e)) return; const hasNext = currentIndex >= 0 && currentIndex < childrenLength - 1; switch (e.key) { case "ArrowRight": e.preventDefault(); if (!e.shiftKey || !mediaRef.current?.seek(SEEK_DELTA_SEC)) { if (hasNext) next(); } break; case "ArrowDown": e.preventDefault(); if (hasNext) next(); break; case "ArrowLeft": e.preventDefault(); if (!e.shiftKey || !mediaRef.current?.seek(-SEEK_DELTA_SEC)) prev(); break; case "ArrowUp": e.preventDefault(); prev(); break; case "Escape": e.preventDefault(); navigate(-1); break; } }; window.addEventListener("keydown", onKeyDown); return () => window.removeEventListener("keydown", onKeyDown); }, [next, prev, currentIndex, childrenLength, mediaRef, navigate]); }