Spaces:
Running
Running
| document.addEventListener('DOMContentLoaded', () => { | |
| const uploadInput = document.getElementById('image-upload'); | |
| const uploadButton = document.getElementById('upload-button'); | |
| const uploadDropzone = document.getElementById('upload-dropzone'); | |
| const uploadedPreviews = document.getElementById('uploaded-previews'); | |
| const imageSpots = Array.from(document.querySelectorAll('.image-spot')); | |
| const textPrompt = document.getElementById('text-prompt'); | |
| const remixButton = document.getElementById('remix-button'); | |
| const statusMessage = document.getElementById('status-message'); | |
| const remixResult = document.getElementById('remix-result'); | |
| const resultImage = document.getElementById('result-image'); | |
| const resultPlaceholder = document.getElementById('result-placeholder'); | |
| // Store base64 data for images in spots | |
| const imageSpotData = Array(3).fill(null); | |
| // Store original draggable elements created from uploaded images | |
| const draggableImages = new Map(); // Map<id, {element, src}> | |
| let uploadedImageCounter = 0; | |
| // --- Utility Functions --- | |
| const showStatus = (message, type = 'loading') => { | |
| statusMessage.textContent = message; | |
| statusMessage.className = `status-message ${type}`; | |
| statusMessage.style.display = 'block'; | |
| }; | |
| const hideStatus = () => { | |
| statusMessage.style.display = 'none'; | |
| statusMessage.textContent = ''; | |
| }; | |
| const clearResult = () => { | |
| resultImage.style.display = 'none'; | |
| resultImage.src = ''; | |
| resultPlaceholder.style.display = 'block'; | |
| }; | |
| // --- File Upload Logic --- | |
| uploadButton.addEventListener('click', () => uploadInput.click()); | |
| uploadInput.addEventListener('change', (event) => { | |
| handleFiles(event.target.files); | |
| }); | |
| uploadDropzone.addEventListener('dragover', (event) => { | |
| event.preventDefault(); | |
| uploadDropzone.classList.add('drag-over'); | |
| }); | |
| uploadDropzone.addEventListener('dragleave', () => { | |
| uploadDropzone.classList.remove('drag-over'); | |
| }); | |
| uploadDropzone.addEventListener('drop', (event) => { | |
| event.preventDefault(); | |
| uploadDropzone.classList.remove('drag-over'); | |
| handleFiles(event.dataTransfer.files); | |
| }); | |
| function handleFiles(files) { | |
| if (files.length === 0) return; | |
| Array.from(files).forEach(file => { | |
| if (!file.type.startsWith('image/')) return; | |
| const reader = new FileReader(); | |
| reader.onload = (e) => { | |
| const imgId = `uploaded-img-${uploadedImageCounter++}`; | |
| createDraggableImage(e.target.result, imgId); | |
| }; | |
| reader.readAsDataURL(file); | |
| }); | |
| } | |
| function createDraggableImage(src, id) { | |
| const wrapper = document.createElement('div'); | |
| wrapper.className = 'uploaded-image-wrapper'; | |
| wrapper.draggable = true; | |
| wrapper.id = id; | |
| const img = document.createElement('img'); | |
| img.src = src; | |
| img.alt = `Uploaded image ${id}`; | |
| const removeBtn = document.createElement('button'); | |
| removeBtn.className = 'remove-image'; | |
| removeBtn.textContent = 'x'; | |
| removeBtn.title = 'Remove image'; | |
| removeBtn.addEventListener('click', (e) => { | |
| e.stopPropagation(); // Prevent drag event from firing | |
| wrapper.remove(); | |
| draggableImages.delete(id); | |
| // Also check if this image was in any spot and remove it | |
| for (let i = 0; i < imageSpotData.length; i++) { | |
| if (imageSpotData[i] && imageSpotData[i].id === id) { | |
| clearImageSpot(imageSpots[i], i); | |
| } | |
| } | |
| }); | |
| wrapper.appendChild(img); | |
| wrapper.appendChild(removeBtn); | |
| uploadedPreviews.appendChild(wrapper); | |
| draggableImages.set(id, { element: wrapper, src: src }); | |
| wrapper.addEventListener('dragstart', (e) => { | |
| e.dataTransfer.setData('text/plain', id); | |
| e.dataTransfer.effectAllowed = 'copyMove'; | |
| // Optional: add a class to the original to make it transparent or similar | |
| // e.target.classList.add('dragging'); | |
| }); | |
| // wrapper.addEventListener('dragend', (e) => { | |
| // e.target.classList.remove('dragging'); | |
| // }); | |
| } | |
| // --- Image Spot Drag & Drop Logic --- | |
| imageSpots.forEach((spot) => { | |
| spot.addEventListener('dragover', (event) => { | |
| event.preventDefault(); | |
| spot.classList.add('drag-over'); | |
| event.dataTransfer.dropEffect = 'copy'; | |
| }); | |
| spot.addEventListener('dragleave', () => { | |
| spot.classList.remove('drag-over'); | |
| }); | |
| spot.addEventListener('drop', (event) => { | |
| event.preventDefault(); | |
| spot.classList.remove('drag-over'); | |
| const draggedId = event.dataTransfer.getData('text/plain'); | |
| const draggedImageData = draggableImages.get(draggedId); | |
| if (draggedImageData) { | |
| const spotIndex = parseInt(spot.dataset.index); | |
| renderImageInSpot(spot, draggedImageData.src); | |
| imageSpotData[spotIndex] = { src: draggedImageData.src, id: draggedId }; | |
| } | |
| }); | |
| }); | |
| function renderImageInSpot(spotElement, imgSrc) { | |
| const imgElement = spotElement.querySelector('.spot-image'); | |
| imgElement.src = imgSrc; | |
| spotElement.classList.add('has-image'); | |
| imgElement.alt = `Image in spot ${spotElement.dataset.index + 1}`; | |
| // Remove old image's alt if it exists and wasn't placeholder. | |
| } | |
| function clearImageSpot(spotElement, spotIndex) { | |
| const imgElement = spotElement.querySelector('.spot-image'); | |
| imgElement.src = 'assets/img/placeholder.png'; // Reset to placeholder | |
| spotElement.classList.remove('has-image'); | |
| imgElement.alt = `Image spot ${spotIndex + 1}`; | |
| imageSpotData[spotIndex] = null; | |
| } | |
| // --- Remix Logic --- | |
| remixButton.addEventListener('click', remixImages); | |
| async function remixImages() { | |
| hideStatus(); | |
| clearResult(); | |
| const images = imageSpotData.filter(data => data !== null); | |
| const prompt = textPrompt.value.trim(); | |
| const selectedModel = document.querySelector('input[name="ai-model"]:checked').value; | |
| if (images.length === 0) { | |
| showStatus('Please drag at least one image into a spot.', 'error'); | |
| return; | |
| } | |
| if (!prompt) { | |
| showStatus('Please enter a text prompt.', 'error'); | |
| return; | |
| } | |
| showStatus(`Remixing with ${selectedModel}... This is a simulation.`, 'loading'); | |
| try { | |
| // Simulate API call | |
| await new Promise(resolve => setTimeout(resolve, 3000)); // Simulate network delay | |
| // In a real application, you would send `images` (base64 or URLs), `prompt`, and `selectedModel` | |
| // to a backend API that integrates with Gemini-2 or GPT Image-1. | |
| // The backend would then return the generated image's URL or base64. | |
| // For simulation, we'll use a placeholder image based on the model | |
| const placeholderResult = selectedModel === 'gemini-2' | |
| ? 'https://picsum.photos/seed/gemini/800/600?grayscale' // A bit more abstract | |
| : 'https://picsum.photos/seed/gpt/800/600'; // Standard realistic | |
| resultImage.src = placeholderResult; | |
| resultImage.style.display = 'block'; | |
| resultPlaceholder.style.display = 'none'; | |
| showStatus('Remix successful! (Simulated)', 'success'); | |
| statusMessage.style.backgroundColor = '#d4edda'; // Light green for success | |
| statusMessage.style.color = '#155724'; | |
| statusMessage.style.borderColor = '#28a745'; | |
| } catch (error) { | |
| console.error('Remix error (simulated):', error); | |
| showStatus('Failed to remix images. Please try again. (Simulated Error)', 'error'); | |
| } | |
| } | |
| }); | |
| // Initial state for result section | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const resultImage = document.getElementById('result-image'); | |
| const resultPlaceholder = document.getElementById('result-placeholder'); | |
| resultImage.style.display = 'none'; | |
| resultPlaceholder.style.display = 'block'; | |
| }); |