"use client" import { useState, useRef, useEffect, useMemo } from "react" import { Upload, X, Loader2 } from "lucide-react" import { Button } from "@/components/ui/button" import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog" import { useToast } from "@/hooks/use-toast" import { validateFileForUpload, generateFilePreview } from "@/lib/file-utils" import { COMIC_STYLES } from "@/lib/constants" interface PageReference { pageNumber: number characterImages: string[] prompt: string imageUrl?: string style: string } interface GeneratePageModalProps { isOpen: boolean onClose: () => void onGenerate: (data: { prompt: string style: string characterFiles?: File[] characterUrls?: string[] isContinuation?: boolean }) => void pageNumber: number allPages: PageReference[] } export function GeneratePageModal({ isOpen, onClose, onGenerate, pageNumber, allPages, }: GeneratePageModalProps) { const [prompt, setPrompt] = useState("") const [uploadedFiles, setUploadedFiles] = useState([]) const [selectedExistingCharacters, setSelectedExistingCharacters] = useState([]) const [referencePageNumber, setReferencePageNumber] = useState(allPages.length > 0 ? allPages.length : 1) const [pageImage, setPageImage] = useState(null) const [previews, setPreviews] = useState([]) const [showPreview, setShowPreview] = useState(null) const [isGenerating, setIsGenerating] = useState(false) const fileInputRef = useRef(null) const { toast } = useToast() const referencePage = allPages.find((p) => p.pageNumber === referencePageNumber) || null const referenceStyleId = referencePage?.style || "noir" const selectedStyleName = COMIC_STYLES.find((s) => s.id === referenceStyleId)?.name || "Noir" const selectedStyle = useMemo(() => selectedStyleName, [referenceStyleId]) useEffect(() => { if (isOpen && referencePage) { setSelectedExistingCharacters(referencePage.characterImages) setPageImage(referencePage.imageUrl || null) } else if (isOpen && !referencePage) { setPageImage(null) } }, [isOpen, referencePage]) const handleFiles = async (newFiles: FileList | null) => { if (!newFiles) return const filesArray = Array.from(newFiles) const validationResults = filesArray.map(file => ({ file, validation: validateFileForUpload(file, true) })) validationResults.forEach(({ validation }) => { if (!validation.valid && validation.error) { toast({ title: "Invalid file", description: validation.error, variant: "destructive", duration: 4000, }) } }) const validFiles = validationResults .filter(({ validation }) => validation.valid) .map(({ file }) => file) if (validFiles.length === 0) return const totalFiles = [...uploadedFiles, ...validFiles].slice(0, 2) setUploadedFiles(totalFiles) const newPreviews = await Promise.all( totalFiles.map((file) => generateFilePreview(file)) ) setPreviews(newPreviews) } const removeFile = (index: number) => { const newFiles = uploadedFiles.filter((_, i) => i !== index) const newPreviews = previews.filter((_, i) => i !== index) setUploadedFiles(newFiles) setPreviews(newPreviews) setShowPreview(null) if (fileInputRef.current) { fileInputRef.current.value = "" } } const toggleExistingCharacter = (characterUrl: string) => { setSelectedExistingCharacters(prev => prev.includes(characterUrl) ? prev.filter(url => url !== characterUrl) : [...prev, characterUrl] ) } const togglePageImage = () => { setPageImage(prev => prev === null && referencePage?.imageUrl ? referencePage.imageUrl : null) } const handleGenerate = async () => { if (!prompt.trim()) return setIsGenerating(true) const fileDataUrls = await Promise.all( uploadedFiles.map((file, index) => { return new Promise((resolve) => { const reader = new FileReader() reader.onload = (e) => { const base64 = e.target?.result as string const [header, base64Data] = base64.split(",") const base64String = base64Data.replace(/_/g, "/") resolve(`data:image/jpeg;base64,${base64String}`) } reader.readAsDataURL(file) }) }) ) const allCharacterUrls: string[] = [ ...selectedExistingCharacters, ...fileDataUrls, ...(pageImage ? [pageImage] : []), ] onGenerate({ prompt, style: selectedStyle, characterFiles: uploadedFiles.length > 0 ? uploadedFiles : undefined, characterUrls: allCharacterUrls, isContinuation: false, }) } useEffect(() => { if (!isOpen) { setIsGenerating(false) setPrompt("") setUploadedFiles([]) setSelectedExistingCharacters([]) setPageImage(null) if (allPages.length > 0) { setReferencePageNumber(allPages.length) } } }, [isOpen]) return ( <> Generate Page {pageNumber}
{/* Reference Page Selection */} {allPages.length > 0 && (
)}
{selectedStyleName}