// Gallery tab — thumbnails from gallery/manifest.json + full-size lightbox. // The manifest is (re)generated by generate_thumbnails.py. document.addEventListener('DOMContentLoaded', () => { const container = document.getElementById('gallery-container'); const lightbox = document.getElementById('lightbox'); const lightboxImg = document.getElementById('lightbox-img'); if (!container || !lightbox) return; const openLightbox = (src) => { lightboxImg.src = src; lightbox.style.display = 'block'; }; const closeLightbox = () => { lightbox.style.display = 'none'; lightboxImg.src = ''; // Clear source to stop GIFs }; // Close on the X, on a click outside the image, or with Escape lightbox.addEventListener('click', (e) => { if (e.target !== lightboxImg) closeLightbox(); }); document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && lightbox.style.display === 'block') closeLightbox(); }); const thumbPath = (file) => `gallery/thumbnails/${file.replace(/\.[^.]+$/, '')}.jpg`; fetch('gallery/manifest.json') .then(res => { if (!res.ok) throw new Error(res.status); return res.json(); }) .then(files => { files.forEach((file, i) => { const img = document.createElement('img'); img.src = thumbPath(file); img.alt = `Gallery image ${i + 1}`; img.loading = 'lazy'; img.onclick = () => openLightbox(`gallery/${file}`); container.appendChild(img); }); }) .catch(err => console.error('Could not load gallery manifest:', err)); });