/** * @license * SPDX-License-Identifier: Apache-2.0 */ import { GoogleGenAI, Modality } from "@google/genai"; // --- DOM Element Selectors --- const imageUpload = document.getElementById("image-upload") as HTMLInputElement; const styleSelect = document.getElementById("style-select") as HTMLSelectElement; const generateButton = document.getElementById( "generate-button" ) as HTMLButtonElement; const beforeImage = document.getElementById("before-image") as HTMLImageElement; const afterImage = document.getElementById("after-image") as HTMLImageElement; const loadingSpinner = document.getElementById( "loading-spinner" ) as HTMLDivElement; const fileNameDisplay = document.getElementById( "file-name" ) as HTMLParagraphElement; // --- State Management --- let uploadedFile: File | null = null; let uploadedFileBase64: string | null = null; // --- Gemini API Initialization --- const ai = new GoogleGenAI({ apiKey: process.env.API_KEY }); const model = "gemini-2.5-flash-image-preview"; // --- Event Listeners --- imageUpload.addEventListener("change", handleImageUpload); generateButton.addEventListener("click", handleGenerateClick); // --- Functions --- /** * Handles the file upload event. */ function handleImageUpload(event: Event) { const target = event.target as HTMLInputElement; const file = target.files?.[0]; if (file) { uploadedFile = file; fileNameDisplay.textContent = file.name; generateButton.disabled = false; const reader = new FileReader(); reader.onloadend = () => { const base64String = (reader.result as string).split(",")[1]; uploadedFileBase64 = base64String; beforeImage.src = reader.result as string; }; reader.readAsDataURL(file); } } /** * Handles the click event for the generate button. */ async function handleGenerateClick() { if (!uploadedFile || !uploadedFileBase64) { alert("Please upload an image first."); return; } setLoading(true); try { const selectedStyle = styleSelect.value; const prompt = `Redesign this room in a ${selectedStyle} style.`; const imagePart = { inlineData: { mimeType: uploadedFile.type, data: uploadedFileBase64, }, }; const textPart = { text: prompt, }; const response = await ai.models.generateContent({ model: model, contents: { parts: [imagePart, textPart], }, config: { responseModalities: [Modality.IMAGE, Modality.TEXT], }, }); // The model can return multiple parts, find the one that is an image. const parts = response.candidates?.[0]?.content?.parts; const imageResult = parts?.find((part) => part.inlineData); if (imageResult && imageResult.inlineData) { const newImageBase64 = imageResult.inlineData.data; const newImageType = imageResult.inlineData.mimeType; afterImage.src = `data:${newImageType};base64,${newImageBase64}`; } else { throw new Error("No image was generated. Please try again."); } } catch (error) { console.error("Error generating design:", error); alert( "An error occurred while generating the design. Please check the console for more details." ); afterImage.src = "https://placehold.co/600x400/ff0000/FFFFFF?text=Error!"; } finally { setLoading(false); } } /** * Toggles the loading state of the UI. * @param isLoading - Whether to show or hide the loading state. */ function setLoading(isLoading: boolean) { if (isLoading) { loadingSpinner.style.display = "flex"; generateButton.disabled = true; generateButton.textContent = "Generating..."; } else { loadingSpinner.style.display = "none"; generateButton.disabled = false; generateButton.textContent = "Generate Design"; } }