"use client"; import React from 'react'; import { useStore } from '@/lib/store'; import { motion, AnimatePresence } from 'framer-motion'; import { Download, Maximize2, Sparkles, Trash2, X, ChevronLeft, ChevronRight, Copy, Film, Wand2 } from 'lucide-react'; import { VideoPromptModal } from './VideoPromptModal'; import { EditPromptModal } from './EditPromptModal'; export function Gallery() { const { gallery, clearGallery, removeFromGallery, setPrompt, addVideo, addToGallery, settings, videos, removeVideo } = useStore(); const [selectedIndex, setSelectedIndex] = React.useState(null); const [videoModalOpen, setVideoModalOpen] = React.useState(false); const [videoSource, setVideoSource] = React.useState<{ data: string, prompt: string } | null>(null); const [editModalOpen, setEditModalOpen] = React.useState(false); const [editSource, setEditSource] = React.useState<{ data: string, prompt: string } | null>(null); const openVideoModal = (img: { data: string, prompt: string }) => { setVideoSource(img); setVideoModalOpen(true); }; const openEditModal = (img: { data: string, prompt: string }) => { setEditSource(img); setEditModalOpen(true); }; const handleGenerateVideo = async (prompt: string) => { if (!videoSource) return; if (!settings.whiskCookies) { alert("Please set your Whisk Cookies in Settings first!"); throw new Error("Missing Whisk cookies"); } const res = await fetch('/api/video/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: prompt, imageBase64: videoSource.data, // imageGenerationId: (videoSource as any).id, // REMOVE: "id" is a local DB ID (e.g. 1), not a Whisk Media ID. cookies: settings.whiskCookies }) }); const data = await res.json(); console.log("[Gallery] Video API response:", data); if (data.success) { console.log("[Gallery] Adding video to store:", { id: data.id, url: data.url?.substring(0, 50) }); addVideo({ id: data.id, url: data.url, prompt: prompt, thumbnail: videoSource.data, // Use source image as thumb createdAt: Date.now() }); // Success notification setTimeout(() => { alert('🎬 Video generation complete!\n\nYour video has been saved. Go to the "Uploads" page and select the "Videos" tab to view it.'); }, 100); } else { console.error(data.error); // Show user-friendly error messages for Google safety policies let errorMessage = data.error; if (data.error?.includes('NCII')) { errorMessage = '🚫 Content Policy: Video blocked by Google\'s NCII (Non-Consensual Intimate Imagery) protection. Please try with a different source image.'; } else if (data.error?.includes('PROMINENT_PEOPLE') || data.error?.includes('prominent')) { errorMessage = '🚫 Content Policy: Video blocked because the image contains a recognizable person. Try using a different image.'; } else if (data.error?.includes('safety') || data.error?.includes('SAFETY')) { errorMessage = '⚠️ Content Policy: Video blocked by Google\'s safety filters. Try a different source image.'; } alert(errorMessage); throw new Error(data.error); } }; const handleRemix = async (prompt: string, options: { keepSubject: boolean; keepScene: boolean; keepStyle: boolean }) => { if (!editSource) return; if (!settings.whiskCookies) { alert("Please set your Whisk Cookies in Settings first!"); throw new Error("Missing Whisk cookies"); } // First upload the current image as a reference const uploadRes = await fetch('/api/references/upload', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ imageBase64: `data:image/png;base64,${editSource.data}`, mimeType: 'image/png', category: 'subject', // Use as subject reference cookies: settings.whiskCookies }) }); const uploadData = await uploadRes.json(); if (!uploadData.id) { throw new Error("Failed to upload reference image"); } // Build refs based on consistency options const refs: { subject?: string[]; scene?: string[]; style?: string[] } = {}; if (options.keepSubject) refs.subject = [uploadData.id]; if (options.keepScene) refs.scene = [uploadData.id]; if (options.keepStyle) refs.style = [uploadData.id]; // Generate new image with references const res = await fetch('/api/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: prompt, aspectRatio: settings.aspectRatio, imageCount: 1, // Generate one remix at a time cookies: settings.whiskCookies, refs: refs }) }); const data = await res.json(); if (data.error) { console.error(data.error); alert("Remix generation failed: " + data.error); throw new Error(data.error); } if (data.images) { for (const img of data.images) { await addToGallery({ data: img.data, prompt: img.prompt, aspectRatio: img.aspectRatio, createdAt: Date.now() }); } } }; React.useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (selectedIndex === null) return; if (e.key === 'Escape') setSelectedIndex(null); if (e.key === 'ArrowLeft') setSelectedIndex(prev => (prev !== null && prev > 0 ? prev - 1 : prev)); if (e.key === 'ArrowRight') setSelectedIndex(prev => (prev !== null && prev < gallery.length - 1 ? prev + 1 : prev)); }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [selectedIndex, gallery.length]); if (gallery.length === 0) { return null; // Or return generic empty state if controlled by parent, but parent checks length usually } const handleClearAll = () => { if (window.confirm("Delete all " + gallery.length + " images?")) { clearGallery(); } }; const selectedImage = selectedIndex !== null ? gallery[selectedIndex] : null; return (
{/* Header with Clear All */}

{gallery.length} Generated Images

{/* Videos Section - Show generated videos */} {videos.length > 0 && (

{videos.length} Generated Video{videos.length > 1 ? 's' : ''}

{videos.map((vid) => ( ))}
)} {/* Gallery Grid */}
{gallery.map((img, i) => ( {img.prompt} setSelectedIndex(i)} loading="lazy" /> {/* Delete button - Top right */} {/* Overlay */}

{img.prompt}

e.stopPropagation()} > {/* Video button - only for 16:9 images */} {img.aspectRatio === '16:9' ? ( ) : ( )}
))}
{/* Lightbox Modal */} {selectedIndex !== null && selectedImage && ( setSelectedIndex(null)} > {/* Close Button */} {/* Navigation Buttons */} {selectedIndex > 0 && ( )} {selectedIndex < gallery.length - 1 && ( )} {/* Image Container */} e.stopPropagation()} > {selectedImage.prompt}

{selectedImage.prompt}

Download Current
)}
{/* Video Modal */} setVideoModalOpen(false)} image={videoSource} onGenerate={handleGenerateVideo} /> {/* Edit/Remix Modal */} setEditModalOpen(false)} image={editSource} onGenerate={handleRemix} />
); }