/* global React */ const { useState, useEffect, useRef, useCallback } = React; function Gallery({ onExit, initialId = null, soundOn = false, onToggleSound = () => {} }) { const photos = window.DARSHAN || []; // resolve initial index from id or default to 0 const startIdx = (() => { if (initialId) { const i = photos.findIndex(p => p.id === initialId); if (i >= 0) return i; } return 0; })(); const [idx, setIdx] = useState(startIdx); const [prevIdx, setPrevIdx] = useState(null); const [transitioning, setTransitioning] = useState(false); const [favs, setFavs] = useState(() => { try { return JSON.parse(localStorage.getItem("darshan.favs") || "[]"); } catch (e) { return []; } }); const [showHelp, setShowHelp] = useState(false); const [shareNote, setShareNote] = useState(""); const [showMeta, setShowMeta] = useState(false); const photo = photos[idx]; const prevPhoto = prevIdx != null ? photos[prevIdx] : null; const isFav = favs.includes(photo?.id); // First-visit help overlay useEffect(() => { try { const seen = localStorage.getItem("darshan.gallery.helpSeen"); if (!seen) { const t = setTimeout(() => { setShowHelp(true); }, 1200); return () => clearTimeout(t); } } catch (e) {} }, []); const dismissHelp = () => { setShowHelp(false); try { localStorage.setItem("darshan.gallery.helpSeen", "1"); } catch (e) {} }; const go = useCallback((d) => { setIdx((cur) => { const next = (cur + d + photos.length) % photos.length; setPrevIdx(cur); setTransitioning(true); setShowMeta(false); setTimeout(() => { setTransitioning(false); setPrevIdx(null); }, 1400); return next; }); }, [photos.length]); const jumpTo = useCallback((i) => { setIdx((cur) => { if (i === cur) return cur; setPrevIdx(cur); setTransitioning(true); setShowMeta(false); setTimeout(() => { setTransitioning(false); setPrevIdx(null); }, 1400); return i; }); }, []); const toggleFav = useCallback(() => { setFavs(cur => { const next = cur.includes(photo.id) ? cur.filter(x => x !== photo.id) : [...cur, photo.id]; try { localStorage.setItem("darshan.favs", JSON.stringify(next)); } catch (e) {} return next; }); }, [photo]); const share = useCallback(() => { const url = `${location.origin}${location.pathname}#darshan/${photo.id}`; const text = `${photo.title} — ${photo.place}\n"${photo.line}"\n${url}`; try { navigator.clipboard.writeText(text); setShareNote("link copied · share gently"); } catch (e) { setShareNote(url); } setTimeout(() => setShareNote(""), 2400); }, [photo]); // keyboard useEffect(() => { const onKey = (e) => { if (showHelp) { if (e.key === "Escape" || e.key === "Enter") { dismissHelp(); return; } } if (e.key === "ArrowRight" || e.key === " ") { e.preventDefault(); go(1); } else if (e.key === "ArrowLeft") { e.preventDefault(); go(-1); } else if (e.key === "Escape") onExit(); else if (e.key === "f" || e.key === "F") toggleFav(); else if (e.key === "s" || e.key === "S") share(); else if (e.key === "i" || e.key === "I") setShowMeta(m => !m); else if (e.key === "?" || e.key === "/") setShowHelp(s => !s); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [go, onExit, toggleFav, share, showHelp]); // wheel — debounced useEffect(() => { let cooling = false; const onWheel = (e) => { if (cooling) return; if (Math.abs(e.deltaY) < 18) return; cooling = true; go(e.deltaY > 0 ? 1 : -1); setTimeout(() => { cooling = false; }, 1100); }; window.addEventListener("wheel", onWheel, { passive: true }); return () => window.removeEventListener("wheel", onWheel); }, [go]); if (!photo) return null; return (
{/* Toast for share */} {shareNote &&
{shareNote}
} {/* Stage */}
{prevPhoto && (
)}
{photo.title}
{/* Side rail · large numeral */}
{photo.id}
{String(photos.length).padStart(2, "0")}
{/* Devanagari watermark */}
{photo.dev}
{/* Caption */}
{photo.place} · {photo.year}

{photo.title}

{photo.line}

{/* Metadata panel — toggle with I or the details button */}
AI · listening
{photo.aiThought}
exposure
{photo.exposure}
coordinates
{photo.coords}
whisper
"{photo.whisper}"
{/* Index strip */}
{photos.map((p, i) => ( ))}
{idx + 1} / {photos.length} · take your time.
{/* Help overlay */} {showHelp && (
e.stopPropagation()}>
— a small map —

how to walk this gallery

walk between photographs
scroll also works · the gallery is gentle
F hold a photograph close
S share a photograph, gently
I open exposure, coordinates & whisper
? open this map again
esc return to the threshold
)}
); } window.Gallery = Gallery;