=== index.html === AI Image Remixer

1. Upload or Drop Images

Drag & drop images here, or click to upload

2. Place Images in Spots

Drag images from the "Uploads" section into these three blank spots.

Spot 1 Image spot 1
Spot 2 Image spot 2
Spot 3 Image spot 3

3. Add a Prompt & Select Model

Choose AI Model:

4. Your Remixed Image

Your remixed image will appear here.

=== assets/css/styles.css === /* Basic Reset & Box Sizing */ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } /* Base Styles */ :root { --primary-color: #6a0dad; /* Dark purple */ --primary-hover: #5a0ca0; --secondary-color: #007bff; /* Blue for highlights */ --text-color: #333; --light-text-color: #f4f4f4; --bg-color: #f9f9f9; --card-bg: #ffffff; --border-color: #e0e0e0; --shadow: 0 4px 10px rgba(0, 0, 0, 0.05); --border-radius: 8px; --spacing-md: 16px; --spacing-lg: 24px; } body { font-family: 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; line-height: 1.6; color: var(--text-color); background-color: var(--bg-color); min-height: 100vh; display: flex; flex-direction: column; } .container { max-width: 1200px; margin: 0 auto; padding: var(--spacing-md); } /* Header */ .site-header { background-color: var(--primary-color); color: var(--light-text-color); padding: var(--spacing-md) 0; box-shadow: var(--shadow); } .site-header .container { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; /* Allow wrapping on smaller screens */ } .site-header h1 { font-size: 1.8em; margin: 0; padding-bottom: 5px; /* Add some padding below for mobile */ } .anycoder-link { color: var(--light-text-color); text-decoration: none; font-weight: bold; font-size: 0.9em; padding: 5px 10px; border: 1px solid var(--light-text-color); border-radius: 5px; transition: background-color 0.3s ease, color 0.3s ease; } .anycoder-link:hover { background-color: var(--light-text-color); color: var(--primary-color); } /* Main Content Sections */ main { flex: 1; /* Allows main content to grow and push footer down */ padding-top: var(--spacing-lg); padding-bottom: var(--spacing-lg); } section { background-color: var(--card-bg); padding: var(--spacing-lg); margin-bottom: var(--spacing-lg); border-radius: var(--border-radius); box-shadow: var(--shadow); } h2 { color: var(--primary-color); font-size: 1.5em; margin-bottom: var(--spacing-md); border-bottom: 2px solid var(--border-color); padding-bottom: 10px; } /* Upload Section */ .upload-section { text-align: center; } .dropzone { border: 2px dashed var(--border-color); border-radius: var(--border-radius); padding: var(--spacing-lg); text-align: center; cursor: pointer; transition: background-color 0.3s ease, border-color 0.3s ease; margin-bottom: var(--spacing-md); position: relative; } .dropzone.drag-over { background-color: #f0e6fa; /* Lighter primary color */ border-color: var(--primary-color); } .dropzone p { margin-bottom: var(--spacing-md); font-size: 1.1em; color: #666; } .btn { display: inline-block; background-color: var(--primary-color); color: var(--light-text-color); padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer; font-size: 1em; transition: background-color 0.3s ease, transform 0.2s ease; } .btn:hover { background-color: var(--primary-hover); transform: translateY(-2px); } .btn-primary { background-color: var(--secondary-color); } .btn-primary:hover { background-color: #0056b3; } .uploaded-previews { display: flex; flex-wrap: wrap; gap: var(--spacing-md); justify-content: center; margin-top: var(--spacing-md); } .uploaded-image-wrapper { position: relative; width: 100px; height: 100px; border: 1px solid var(--border-color); border-radius: var(--border-radius); overflow: hidden; cursor: grab; box-shadow: 0 2px 5px rgba(0,0,0,0.1); } .uploaded-image-wrapper img { width: 100%; height: 100%; object-fit: cover; display: block; } .remove-image { position: absolute; top: 5px; right: 5px; background-color: rgba(255, 0, 0, 0.7); color: white; border: none; border-radius: 50%; width: 20px; height: 20px; font-size: 0.8em; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: background-color 0.2s ease; } .remove-image:hover { background-color: rgba(255, 0, 0, 1); } /* Remix Spots Section */ .instruction-text { font-style: italic; color: #666; margin-bottom: var(--spacing-md); text-align: center; } .remix-spots { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--spacing-md); margin-top: var(--spacing-md); } .image-spot { border: 2px dashed var(--border-color); border-radius: var(--border-radius); min-height: 200px; display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; padding: var(--spacing-md); transition: background-color 0.3s ease, border-color 0.3s ease; background-color: #fefefe; position: relative; overflow: hidden; /* Ensure image doesn't overflow */ } .image-spot.drag-over { background-color: #e6f0fa; /* Lighter blue for spot drag-over */ border-color: var(--secondary-color); } .image-spot .spot-label { position: absolute; top: 10px; left: 10px; background-color: rgba(0, 0, 0, 0.6); color: white; padding: 3px 8px; border-radius: 5px; font-size: 0.8em; z-index: 10; } .image-spot .spot-image { max-width: 100%; max-height: 180px; /* Constrain image height */ object-fit: contain; display: block; margin-top: 20px; /* Space for label */ filter: grayscale(80%); /* Placeholder initial look */ transition: filter 0.3s ease; } .image-spot.has-image .spot-image { filter: grayscale(0%); /* Full color when an image is dropped */ } /* Prompt & Model Section */ .prompt-input-group { margin-bottom: var(--spacing-lg); } .prompt-input-group label { display: block; font-weight: bold; margin-bottom: 8px; color: var(--primary-color); } textarea#text-prompt { width: 100%; padding: 12px; border: 1px solid var(--border-color); border-radius: var(--border-radius); font-size: 1em; font-family: inherit; resize: vertical; min-height: 100px; transition: border-color 0.3s ease; } textarea#text-prompt:focus { outline: none; border-color: var(--primary-color); box-shadow: 0 0 0 3px rgba(106, 13, 173, 0.2); } .model-selection h3 { font-size: 1.2em; color: var(--primary-color); margin-bottom: var(--spacing-md); } .model-options { display: flex; gap: var(--spacing-md); flex-wrap: wrap; margin-bottom: var(--spacing-lg); } .model-radio { display: flex; align-items: center; border: 1px solid var(--border-color); border-radius: var(--border-radius); padding: 10px 15px; cursor: pointer; transition: background-color 0.3s ease, border-color 0.3s ease; background-color: var(--card-bg); position: relative; overflow: visible; /* Allow tooltip to show */ } .model-radio:hover { background-color: #f0f0f0; } .model-radio input[type="radio"] { margin-right: 8px; accent-color: var(--primary-color); /* Custom color for radio button */ } .model-radio input[type="radio"]:checked + span { font-weight: bold; color: var(--primary-color); } /* Tooltip for model description */ .model-radio .tooltip { visibility: hidden; opacity: 0; width: 180px; background-color: rgba(0, 0, 0, 0.8); color: #fff; text-align: center; border-radius: 6px; padding: 8px 0; position: absolute; z-index: 1; bottom: 125%; /* Position above the text */ left: 50%; margin-left: -90px; /* Center the tooltip */ font-size: 0.8em; transition: opacity 0.3s; } .model-radio .tooltip::after { content: " "; position: absolute; top: 100%; /* At the bottom of the tooltip */ left: 50%; margin-left: -5px; border-width: 5px; border-style: solid; border-color: rgba(0, 0, 0, 0.8) transparent transparent transparent; } .model-radio:hover .tooltip { visibility: visible; opacity: 1; } /* Status Message */ .status-message { margin-top: var(--spacing-md); padding: 10px 15px; border-radius: var(--border-radius); font-weight: bold; text-align: center; display: none; /* Hidden by default */ } .status-message.loading { background-color: #e0f7fa; /* Light blue */ color: #007bff; border: 1px solid #007bff; display: block; } .status-message.error { background-color: #ffebee; /* Light red */ color: #d32f2f; border: 1px solid #d32f2f; display: block; } /* Result Section */ .remix-result { min-height: 250px; display: flex; justify-content: center; align-items: center; border: 2px dashed var(--border-color); border-radius: var(--border-radius); background-color: #fefefe; overflow: hidden; padding: var(--spacing-md); } .remix-result img#result-image { max-width: 100%; max-height: 500px; /* Max height for the result image */ height: auto; border-radius: var(--border-radius); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); } .remix-result p#result-placeholder { color: #888; font-style: italic; } /* Footer */ .site-footer { background-color: var(--text-color); color: var(--light-text-color); text-align: center; padding: var(--spacing-md) 0; margin-top: var(--spacing-lg); } .site-footer p { font-size: 0.9em; } /* Responsive Adjustments */ @media (max-width: 768px) { .site-header .container { flex-direction: column; text-align: center; } .site-header h1 { margin-bottom: var(--spacing-md); } .remix-spots { grid-template-columns: 1fr; /* Stack spots vertically */ } .model-options { flex-direction: column; /* Stack model options vertically */ } } @media (max-width: 480px) { .container { padding: var(--spacing-md); } h1 { font-size: 1.5em; } h2 { font-size: 1.3em; } .btn { width: 100%; margin-top: var(--spacing-md); } .uploaded-image-wrapper { width: 80px; height: 80px; } } === assets/js/script.js === 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 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}...`, '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!', 'success'); statusMessage.style.backgroundColor = '#d4edda'; // Light green for success statusMessage.style.color = '#155724'; statusMessage.style.borderColor = '#28a745'; } catch (error) { console.error('Remix error:', error); showStatus('Failed to remix images. Please try again.', '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'; }); === assets/img/placeholder.png === iVBORw0KGgoAAAANSUhEUgAAASwAAAEsAQMAAABHRxZqAAAAA1BMVEWAgIBN1FfXAAAAFUlEQVR4XnOgWAAAAqIApQAAAD8JvL4AAAAASUVORK5CYII= === assets/img/initial-result.png === iVBORw0KGgoAAAANSUhEUgAAASwAAAEsAQMAAABHRxZqAAAAA1BMVEWAgIBN1FfXAAAAFUlEQVR4XnOgWAAAAqIApQAAAD8JvL4AAAAASUVORK5CYII=