// Helper function to convert file to base64 function fileToBase64(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => { // Remove data:image/...;base64, prefix if present const base64 = reader.result.split(',')[1] || reader.result; resolve(base64); }; reader.onerror = reject; reader.readAsDataURL(file); }); } // Helper function to read multiple files async function filesToBase64Array(fileList) { const base64Array = []; for (let i = 0; i < fileList.length; i++) { const base64 = await fileToBase64(fileList[i]); base64Array.push(base64); } return base64Array; } function genImage(event) { event.preventDefault(); promptDallE(); } async function promptDallE() { const loader = document.getElementById("loader"); const input = document.getElementById("input"); const generateBtn = document.getElementById("generate-btn"); const resultDiv = document.getElementById("result"); const resultPlaceholder = document.getElementById("result-placeholder"); // Show loader and disable form loader.classList.remove("hidden"); if (resultPlaceholder) { resultPlaceholder.style.display = "none"; } input.disabled = true; generateBtn.disabled = true; // Store the prompt for later restoration const prompt = input.value.trim(); if (!prompt) { alert("Please enter a prompt"); loader.classList.add("hidden"); if (resultPlaceholder) { resultPlaceholder.style.display = "flex"; } input.disabled = false; generateBtn.disabled = false; return; } // Collect all form values const model = document.getElementById("image-model").value; const size = document.getElementById("image-size").value; const negativePrompt = document.getElementById("negative-prompt").value.trim(); const n = parseInt(document.getElementById("image-count").value) || 1; const stepInput = document.getElementById("image-steps").value.trim(); const step = stepInput ? parseInt(stepInput) : undefined; const seedInput = document.getElementById("image-seed").value.trim(); const seed = seedInput ? parseInt(seedInput) : undefined; // Prepare request body // Combine prompt and negative prompt with "|" separator (backend expects this format) let combinedPrompt = prompt; if (negativePrompt) { combinedPrompt = prompt + "|" + negativePrompt; } const requestBody = { model: model, prompt: combinedPrompt, n: n, size: size, }; if (step !== undefined) { requestBody.step = step; } if (seed !== undefined) { requestBody.seed = seed; } // Handle file inputs try { // Source image (single file for img2img) const sourceImageInput = document.getElementById("source-image"); if (sourceImageInput.files.length > 0) { const base64 = await fileToBase64(sourceImageInput.files[0]); requestBody.file = base64; } // Reference images (collect from all dynamic inputs) const refImageInputs = document.querySelectorAll('.reference-image-file'); const refImageFiles = []; for (const input of refImageInputs) { if (input.files.length > 0) { refImageFiles.push(input.files[0]); } } if (refImageFiles.length > 0) { const base64Array = await filesToBase64Array(refImageFiles); requestBody.ref_images = base64Array; } } catch (error) { console.error("Error processing image files:", error); resultDiv.innerHTML = '

Error processing image files: ' + error.message + '

'; loader.classList.add("hidden"); if (resultPlaceholder) { resultPlaceholder.style.display = "none"; } input.disabled = false; generateBtn.disabled = false; return; } // Make API request try { const response = await fetch("v1/images/generations", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(requestBody), }); const json = await response.json(); if (json.error) { // Display error resultDiv.innerHTML = '

Error: ' + json.error.message + '

'; loader.classList.add("hidden"); if (resultPlaceholder) { resultPlaceholder.style.display = "none"; } input.disabled = false; generateBtn.disabled = false; return; } // Clear result div and hide placeholder resultDiv.innerHTML = ''; if (resultPlaceholder) { resultPlaceholder.style.display = "none"; } // Display all generated images if (json.data && json.data.length > 0) { json.data.forEach((item, index) => { const imageContainer = document.createElement("div"); imageContainer.className = "flex flex-col"; // Create image element const img = document.createElement("img"); if (item.url) { img.src = item.url; } else if (item.b64_json) { img.src = "data:image/png;base64," + item.b64_json; } else { return; // Skip invalid items } img.alt = prompt; img.className = "w-full h-auto rounded-lg"; imageContainer.appendChild(img); // Create caption container (optional, can be collapsed or shown on hover) const captionDiv = document.createElement("div"); captionDiv.className = "mt-2 p-2 bg-[var(--color-bg-secondary)] rounded-lg text-xs"; // Prompt caption const promptCaption = document.createElement("p"); promptCaption.className = "text-[var(--color-text-primary)] mb-1.5 break-words"; promptCaption.innerHTML = 'Prompt: ' + escapeHtml(prompt); captionDiv.appendChild(promptCaption); // Negative prompt if provided if (negativePrompt) { const negativeCaption = document.createElement("p"); negativeCaption.className = "text-[var(--color-text-secondary)] mb-1.5 break-words"; negativeCaption.innerHTML = 'Negative Prompt: ' + escapeHtml(negativePrompt); captionDiv.appendChild(negativeCaption); } // Generation details const detailsDiv = document.createElement("div"); detailsDiv.className = "flex flex-wrap gap-3 text-[10px] text-[var(--color-text-secondary)] mt-1.5"; detailsDiv.innerHTML = ` Size: ${size} ${step !== undefined ? `Steps: ${step}` : ''} ${seed !== undefined ? `Seed: ${seed}` : ''} `; captionDiv.appendChild(detailsDiv); // Copy prompt button const copyBtn = document.createElement("button"); copyBtn.className = "mt-1.5 px-2 py-0.5 text-[10px] bg-[var(--color-primary)] text-white rounded hover:opacity-80"; copyBtn.innerHTML = 'Copy Prompt'; copyBtn.onclick = () => { navigator.clipboard.writeText(prompt).then(() => { copyBtn.innerHTML = 'Copied!'; setTimeout(() => { copyBtn.innerHTML = 'Copy Prompt'; }, 2000); }); }; captionDiv.appendChild(copyBtn); imageContainer.appendChild(captionDiv); resultDiv.appendChild(imageContainer); }); // Hide placeholder when images are displayed if (resultPlaceholder) { resultPlaceholder.style.display = "none"; } } else { resultDiv.innerHTML = '

No images were generated.

'; if (resultPlaceholder) { resultPlaceholder.style.display = "none"; } } // Preserve prompt in input field (don't clear it) // The prompt is already in the input field, so we don't need to restore it } catch (error) { console.error("Error generating image:", error); resultDiv.innerHTML = '

Error: ' + error.message + '

'; if (resultPlaceholder) { resultPlaceholder.style.display = "none"; } } finally { // Hide loader and re-enable form loader.classList.add("hidden"); input.disabled = false; generateBtn.disabled = false; input.focus(); } } // Helper function to escape HTML function escapeHtml(text) { const div = document.createElement("div"); div.textContent = text; return div.innerHTML; } // Initialize document.addEventListener("DOMContentLoaded", function() { const input = document.getElementById("input"); const form = document.getElementById("genimage"); if (input) { input.focus(); } if (form) { form.addEventListener("submit", genImage); } // Handle Enter key press in the prompt input (but allow Shift+Enter for new lines) if (input) { input.addEventListener("keydown", function(event) { if (event.key === "Enter" && !event.shiftKey) { event.preventDefault(); genImage(event); } }); } // Hide loader initially const loader = document.getElementById("loader"); if (loader) { loader.classList.add("hidden"); } });