| | <!DOCTYPE html> |
| | <html lang="en"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>PixelWizard AI Image Editor ✨</title> |
| | <link rel="icon" type="image/x-icon" href="/static/favicon.ico"> |
| | <script src="https://cdn.tailwindcss.com"></script> |
| | <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> |
| | <script src="https://unpkg.com/feather-icons"></script> |
| | <script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.globe.min.js"></script> |
| | <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> |
| | <style> |
| | body { |
| | font-family: 'Inter', sans-serif; |
| | background-color: #111827; |
| | color: #f3f4f6; |
| | } |
| | .dropzone { |
| | border: 2px dashed #06b6d4; |
| | border-radius: 0.5rem; |
| | transition: all 0.3s ease; |
| | } |
| | .dropzone-active { |
| | border-color: #0ea5e9; |
| | background-color: rgba(6, 182, 212, 0.1); |
| | } |
| | .vanta-globe { |
| | position: absolute; |
| | top: 0; |
| | left: 0; |
| | width: 100%; |
| | height: 100%; |
| | z-index: -1; |
| | opacity: 0.2; |
| | } |
| | .toast { |
| | animation: slideUp 0.3s ease-out; |
| | } |
| | @keyframes slideUp { |
| | from { transform: translateY(100%); opacity: 0; } |
| | to { transform: translateY(0); opacity: 1; } |
| | } |
| | .image-mask { |
| | position: relative; |
| | } |
| | .image-mask::after { |
| | content: ''; |
| | position: absolute; |
| | top: 0; |
| | left: 0; |
| | right: 0; |
| | bottom: 0; |
| | background-color: rgba(6, 182, 212, 0.3); |
| | pointer-events: none; |
| | } |
| | </style> |
| | </head> |
| | <body> |
| | <div class="vanta-globe" id="vanta-globe"></div> |
| | |
| | <div class="min-h-screen flex flex-col"> |
| | |
| | <header class="bg-gray-900/80 backdrop-blur-md border-b border-gray-800 fixed w-full z-10"> |
| | <div class="container mx-auto px-4 py-3 flex justify-between items-center"> |
| | <h1 class="text-2xl font-bold text-cyan-400 flex items-center"> |
| | <i data-feather="wand-2" class="mr-2"></i> |
| | PixelWizard AI |
| | </h1> |
| | <a href="https://github.com" target="_blank" class="text-gray-400 hover:text-cyan-400 transition-colors"> |
| | <i data-feather="github"></i> |
| | </a> |
| | </div> |
| | </header> |
| |
|
| | |
| | <main class="flex-grow container mx-auto px-4 pt-20 pb-8 flex flex-col lg:flex-row gap-6"> |
| | |
| | <div class="w-full lg:w-96 bg-gray-800/80 backdrop-blur-md rounded-xl border border-gray-700 p-6 flex flex-col"> |
| | |
| | <div class="space-y-6"> |
| | |
| | <div class="space-y-3"> |
| | <h2 class="text-lg font-semibold text-gray-200">1. Upload Your Photo</h2> |
| | <div id="dropzone" class="dropzone h-48 flex flex-col items-center justify-center p-4 cursor-pointer"> |
| | <i data-feather="upload" class="text-cyan-400 w-8 h-8 mb-2"></i> |
| | <p class="text-center text-gray-300">Drag & drop your image here</p> |
| | <p class="text-xs text-gray-500 mt-1">or click to browse</p> |
| | <input type="file" id="image-upload" class="hidden" accept="image/*"> |
| | </div> |
| | <button id="face-protect-btn" class="hidden w-full py-2 px-4 bg-gray-700 hover:bg-gray-600 text-cyan-400 rounded-lg transition-colors flex items-center justify-center"> |
| | <i data-feather="shield" class="mr-2"></i> |
| | Protect Faces (Manual) |
| | </button> |
| | <div id="face-protect-success" class="hidden text-green-400 text-sm flex items-center"> |
| | <i data-feather="check-circle" class="mr-1"></i> |
| | Face protection active |
| | </div> |
| | </div> |
| |
|
| | |
| | <div id="reference-section" class="hidden space-y-3"> |
| | <h2 class="text-lg font-semibold text-gray-200">2. Style Reference (Optional)</h2> |
| | <div id="reference-dropzone" class="dropzone h-32 flex flex-col items-center justify-center p-4 cursor-pointer"> |
| | <i data-feather="image" class="text-cyan-400 w-6 h-6 mb-2"></i> |
| | <p class="text-center text-sm text-gray-300">Drag & drop reference image</p> |
| | </div> |
| | <input type="text" id="reference-name" placeholder="e.g., 'Mhomt shirt'" class="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-lg text-gray-200 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-cyan-500"> |
| | </div> |
| |
|
| | |
| | <div class="space-y-3"> |
| | <h2 class="text-lg font-semibold text-gray-200">3. Describe Your Edits</h2> |
| | <textarea id="edit-prompt" rows="4" class="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-lg text-gray-200 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-cyan-500" placeholder="e.g., 'Change the shirt color to blue and add a futuristic background'"></textarea> |
| | </div> |
| |
|
| | |
| | <div id="elements-section" class="hidden space-y-3"> |
| | <h2 class="text-lg font-semibold text-gray-200">4. Elements to Edit (Optional)</h2> |
| | <div id="analyze-prompt" class="p-4 bg-gray-700 rounded-lg text-center cursor-pointer hover:bg-gray-600 transition-colors"> |
| | <p class="text-gray-300">Click on the original image to analyze elements</p> |
| | </div> |
| | <div id="analyzing" class="hidden p-4 bg-gray-700 rounded-lg text-center"> |
| | <div class="flex items-center justify-center space-x-2"> |
| | <div class="animate-spin"> |
| | <i data-feather="loader"></i> |
| | </div> |
| | <p class="text-gray-300">Analyzing image...</p> |
| | </div> |
| | </div> |
| | <div id="elements-list" class="hidden space-y-2"> |
| | |
| | </div> |
| | <div id="mask-active" class="hidden text-cyan-400 text-sm flex items-center"> |
| | <i data-feather="check-circle" class="mr-1"></i> |
| | Precision mask is active |
| | </div> |
| | </div> |
| |
|
| | |
| | <div class="space-y-3"> |
| | <h2 class="text-lg font-semibold text-gray-200">5. Render Profile (Optional)</h2> |
| | <select id="render-profile" class="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-lg text-gray-200 focus:outline-none focus:ring-2 focus:ring-cyan-500"> |
| | <option value="default">Default</option> |
| | <option value="iphone">iPhone 14 Pro</option> |
| | <option value="pro">Professional Camera</option> |
| | <option value="film">Film Look</option> |
| | <option value="illustration">Illustration Style</option> |
| | </select> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div class="mt-auto pt-6 space-y-3"> |
| | <button id="optimize-prompt-btn" class="w-full py-3 px-4 border border-cyan-400 text-cyan-400 rounded-lg hover:bg-cyan-400/10 transition-colors flex items-center justify-center"> |
| | <i data-feather="sparkles" class="mr-2"></i> |
| | Optimize Prompt |
| | </button> |
| | <button id="apply-magic-btn" class="w-full py-3 px-4 bg-cyan-500 hover:bg-cyan-600 text-gray-900 font-medium rounded-lg transition-colors flex items-center justify-center"> |
| | <i data-feather="zap" class="mr-2"></i> |
| | Apply Magic |
| | </button> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div class="flex-grow bg-gray-800/80 backdrop-blur-md rounded-xl border border-gray-700 p-6 flex flex-col"> |
| | <div class="flex-grow grid grid-cols-1 md:grid-cols-2 gap-4"> |
| | |
| | <div class="relative bg-gray-900 rounded-lg overflow-hidden"> |
| | <div class="absolute top-3 left-3 z-10 flex space-x-2"> |
| | <button class="p-2 bg-gray-800/80 rounded-full hover:bg-gray-700/80 transition-colors"> |
| | <i data-feather="zoom-in" class="w-4 h-4 text-gray-300"></i> |
| | </button> |
| | <button class="p-2 bg-gray-800/80 rounded-full hover:bg-gray-700/80 transition-colors"> |
| | <i data-feather="zoom-out" class="w-4 h-4 text-gray-300"></i> |
| | </button> |
| | <button class="p-2 bg-gray-800/80 rounded-full hover:bg-gray-700/80 transition-colors"> |
| | <i data-feather="refresh-cw" class="w-4 h-4 text-gray-300"></i> |
| | </button> |
| | <button id="mask-toggle" class="p-2 bg-gray-800/80 rounded-full hover:bg-gray-700/80 transition-colors"> |
| | <i data-feather="edit-3" class="w-4 h-4 text-gray-300"></i> |
| | </button> |
| | </div> |
| | <div id="original-container" class="h-full flex items-center justify-center"> |
| | <p class="text-gray-500">Original image will appear here</p> |
| | </div> |
| | <canvas id="mask-canvas" class="absolute inset-0 w-full h-full hidden"></canvas> |
| | </div> |
| |
|
| | |
| | <div class="relative bg-gray-900 rounded-lg overflow-hidden"> |
| | <div id="edited-container" class="h-full flex items-center justify-center"> |
| | <p class="text-gray-500">Edited image will appear here</p> |
| | </div> |
| | <div id="processing" class="hidden absolute inset-0 flex items-center justify-center bg-gray-900/80"> |
| | <div class="text-center"> |
| | <div class="animate-spin mx-auto mb-3"> |
| | <i data-feather="loader" class="w-8 h-8 text-cyan-400"></i> |
| | </div> |
| | <p class="text-gray-300">Creating magic...</p> |
| | <p class="text-xs text-gray-500">This may take a moment</p> |
| | </div> |
| | </div> |
| | <button id="download-btn" class="hidden absolute bottom-3 right-3 p-2 bg-cyan-500 hover:bg-cyan-600 rounded-full transition-colors"> |
| | <i data-feather="download" class="w-4 h-4 text-gray-900"></i> |
| | </button> |
| | </div> |
| | </div> |
| | </div> |
| | </main> |
| |
|
| | |
| | <div id="mask-tools" class="fixed inset-0 bg-gray-900/90 backdrop-blur-md z-20 hidden"> |
| | <div class="absolute top-4 left-1/2 transform -translate-x-1/2 bg-gray-800 rounded-full px-4 py-2 flex space-x-3"> |
| | <button id="brush-tool" class="p-2 bg-cyan-500/20 rounded-full hover:bg-cyan-500/30 transition-colors text-cyan-400"> |
| | <i data-feather="edit-2" class="w-4 h-4"></i> |
| | </button> |
| | <button id="erase-tool" class="p-2 bg-gray-700 rounded-full hover:bg-gray-600 transition-colors text-gray-300"> |
| | <i data-feather="eraser" class="w-4 h-4"></i> |
| | </button> |
| | <div class="flex items-center space-x-2"> |
| | <i data-feather="minimize-2" class="w-4 h-4 text-gray-400"></i> |
| | <input type="range" id="brush-size" min="1" max="50" value="10" class="w-24"> |
| | <i data-feather="maximize-2" class="w-4 h-4 text-gray-400"></i> |
| | </div> |
| | <button id="clear-mask" class="p-2 bg-gray-700 rounded-full hover:bg-gray-600 transition-colors text-gray-300"> |
| | <i data-feather="trash-2" class="w-4 h-4"></i> |
| | </button> |
| | <button id="save-mask" class="p-2 bg-cyan-500 rounded-full hover:bg-cyan-600 transition-colors text-gray-900"> |
| | <i data-feather="check" class="w-4 h-4"></i> |
| | </button> |
| | <button id="cancel-mask" class="p-2 bg-gray-700 rounded-full hover:bg-gray-600 transition-colors text-gray-300"> |
| | <i data-feather="x" class="w-4 h-4"></i> |
| | </button> |
| | </div> |
| | </div> |
| |
|
| | |
| | <div id="toast" class="fixed bottom-4 left-4 hidden"> |
| | <div class="bg-gray-800 border rounded-lg shadow-lg px-4 py-3 flex items-start max-w-xs"> |
| | <div class="flex-grow"> |
| | <div class="flex items-center"> |
| | <i data-feather="alert-circle" class="text-red-400 mr-2"></i> |
| | <p id="toast-message" class="text-sm text-gray-200">Error message here</p> |
| | </div> |
| | </div> |
| | <button id="close-toast" class="ml-3 text-gray-400 hover:text-gray-300"> |
| | <i data-feather="x" class="w-4 h-4"></i> |
| | </button> |
| | </div> |
| | </div> |
| | </div> |
| | <script> |
| | |
| | function applyEdits() { |
| | const originalImg = document.querySelector('#original-container img'); |
| | const prompt = document.getElementById('edit-prompt').value.toLowerCase(); |
| | |
| | if (!originalImg) { |
| | showToast('Please upload an image first', 'error'); |
| | return false; |
| | } |
| | |
| | const editedImg = originalImg.cloneNode(true); |
| | let hasChanges = false; |
| | |
| | |
| | if (prompt.includes('color') || prompt.includes('colour')) { |
| | if (prompt.includes('blue')) { |
| | editedImg.style.filter = 'hue-rotate(180deg)'; |
| | hasChanges = true; |
| | } else if (prompt.includes('red')) { |
| | editedImg.style.filter = 'hue-rotate(-90deg)'; |
| | hasChanges = true; |
| | } else if (prompt.includes('green')) { |
| | editedImg.style.filter = 'hue-rotate(90deg)'; |
| | hasChanges = true; |
| | } |
| | } |
| | |
| | if (prompt.includes('brighten') || prompt.includes('lighten')) { |
| | editedImg.style.filter = (editedImg.style.filter || '') + ' brightness(1.2)'; |
| | hasChanges = true; |
| | } |
| | |
| | if (prompt.includes('darken')) { |
| | editedImg.style.filter = (editedImg.style.filter || '') + ' brightness(0.8)'; |
| | hasChanges = true; |
| | } |
| | |
| | if (prompt.includes('blur') || prompt.includes('background')) { |
| | editedImg.style.filter = (editedImg.style.filter || '') + ' blur(4px)'; |
| | hasChanges = true; |
| | } |
| | |
| | if (!hasChanges) { |
| | editedImg.style.transform = 'scaleX(-1)'; |
| | } |
| | |
| | return editedImg; |
| | } |
| | |
| | |
| | VANTA.GLOBE({ |
| | el: "#vanta-globe", |
| | mouseControls: true, |
| | touchControls: true, |
| | gyroControls: false, |
| | minHeight: 200.00, |
| | minWidth: 200.00, |
| | scale: 1.00, |
| | scaleMobile: 1.00, |
| | color: 0x06b6d4, |
| | backgroundColor: 0x111827, |
| | size: 0.8 |
| | }); |
| | |
| | |
| | document.addEventListener('DOMContentLoaded', function() { |
| | feather.replace(); |
| | }); |
| | |
| | |
| | document.getElementById('image-upload').addEventListener('change', function(e) { |
| | const file = e.target.files[0]; |
| | if (file) { |
| | const reader = new FileReader(); |
| | reader.onload = function(event) { |
| | const img = document.createElement('img'); |
| | img.src = event.target.result; |
| | img.className = 'max-w-full max-h-full'; |
| | |
| | const container = document.getElementById('original-container'); |
| | container.innerHTML = ''; |
| | container.appendChild(img); |
| | |
| | |
| | document.getElementById('face-protect-btn').classList.remove('hidden'); |
| | document.getElementById('reference-section').classList.remove('hidden'); |
| | document.getElementById('elements-section').classList.remove('hidden'); |
| | |
| | |
| | showToast('Image uploaded successfully!'); |
| | }; |
| | reader.readAsDataURL(file); |
| | } |
| | }); |
| | |
| | document.getElementById('dropzone').addEventListener('click', function() { |
| | document.getElementById('image-upload').click(); |
| | }); |
| | |
| | document.getElementById('face-protect-btn').addEventListener('click', function() { |
| | this.classList.add('hidden'); |
| | document.getElementById('face-protect-success').classList.remove('hidden'); |
| | showToast('Face protection activated'); |
| | }); |
| | document.getElementById('apply-magic-btn').addEventListener('click', function() { |
| | document.getElementById('processing').classList.remove('hidden'); |
| | document.getElementById('edited-container').innerHTML = '<p class="text-gray-500">Edited image will appear here</p>'; |
| | |
| | setTimeout(function() { |
| | document.getElementById('processing').classList.add('hidden'); |
| | |
| | const editedImg = applyEdits(); |
| | if (editedImg) { |
| | document.getElementById('edited-container').innerHTML = ''; |
| | document.getElementById('edited-container').appendChild(editedImg); |
| | document.getElementById('download-btn').classList.remove('hidden'); |
| | showToast('Edits applied successfully!'); |
| | } |
| | }, 2000); |
| | }); |
| | document.getElementById('mask-toggle').addEventListener('click', function() { |
| | document.getElementById('mask-tools').classList.remove('hidden'); |
| | document.body.style.overflow = 'hidden'; |
| | }); |
| | |
| | document.getElementById('cancel-mask').addEventListener('click', function() { |
| | document.getElementById('mask-tools').classList.add('hidden'); |
| | document.body.style.overflow = ''; |
| | }); |
| | |
| | document.getElementById('save-mask').addEventListener('click', function() { |
| | document.getElementById('mask-tools').classList.add('hidden'); |
| | document.body.style.overflow = ''; |
| | document.getElementById('elements-list').innerHTML = ''; |
| | document.getElementById('mask-active').classList.remove('hidden'); |
| | showToast('Precision mask saved'); |
| | }); |
| | |
| | document.getElementById('download-btn').addEventListener('click', function() { |
| | showToast('Download started'); |
| | }); |
| | document.getElementById('optimize-prompt-btn').addEventListener('click', function() { |
| | const prompt = document.getElementById('edit-prompt').value; |
| | if (prompt.trim() === '') { |
| | showToast('Please enter a prompt first', 'error'); |
| | return; |
| | } |
| | |
| | showToast('Optimizing your prompt...'); |
| | setTimeout(function() { |
| | let optimized = `Professional edit: ${prompt}. `; |
| | |
| | if (prompt.toLowerCase().includes('color') || prompt.toLowerCase().includes('colour')) { |
| | optimized += 'Using precise color adjustments. '; |
| | } |
| | if (prompt.toLowerCase().includes('background')) { |
| | optimized += 'With enhanced background separation. '; |
| | } |
| | if (prompt.toLowerCase().includes('face') || prompt.toLowerCase().includes('portrait')) { |
| | optimized += 'Maintaining natural skin tones. '; |
| | } |
| | |
| | optimized += 'Preserving original composition and lighting.'; |
| | document.getElementById('edit-prompt').value = optimized; |
| | showToast('Prompt optimized!'); |
| | }, 1500); |
| | }); |
| | |
| | function showToast(message, type = 'success') { |
| | const toast = document.getElementById('toast'); |
| | const toastMessage = document.getElementById('toast-message'); |
| | const toastIcon = document.querySelector('#toast i'); |
| | |
| | toastMessage.textContent = message; |
| | |
| | if (type === 'error') { |
| | toastIcon.setAttribute('data-feather', 'alert-triangle'); |
| | toastIcon.classList.remove('text-green-400', 'text-yellow-400'); |
| | toastIcon.classList.add('text-red-400'); |
| | toast.classList.remove('border-green-500'); |
| | toast.classList.add('border-red-500'); |
| | } else if (type === 'warning') { |
| | toastIcon.setAttribute('data-feather', 'alert-circle'); |
| | toastIcon.classList.remove('text-green-400', 'text-red-400'); |
| | toastIcon.classList.add('text-yellow-400'); |
| | toast.classList.remove('border-green-500', 'border-red-500'); |
| | toast.classList.add('border-yellow-500'); |
| | } else { |
| | toastIcon.setAttribute('data-feather', 'check-circle'); |
| | toastIcon.classList.remove('text-red-400', 'text-yellow-400'); |
| | toastIcon.classList.add('text-green-400'); |
| | toast.classList.remove('border-red-500', 'border-yellow-500'); |
| | toast.classList.add('border-green-500'); |
| | } |
| | feather.replace(); |
| | toast.classList.remove('hidden'); |
| | |
| | setTimeout(function() { |
| | toast.classList.add('hidden'); |
| | }, 5000); |
| | } |
| | |
| | document.getElementById('close-toast').addEventListener('click', function() { |
| | document.getElementById('toast').classList.add('hidden'); |
| | }); |
| | |
| | |
| | ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { |
| | document.getElementById('dropzone').addEventListener(eventName, preventDefaults, false); |
| | }); |
| | |
| | function preventDefaults(e) { |
| | e.preventDefault(); |
| | e.stopPropagation(); |
| | } |
| | |
| | ['dragenter', 'dragover'].forEach(eventName => { |
| | document.getElementById('dropzone').addEventListener(eventName, highlight, false); |
| | }); |
| | |
| | ['dragleave', 'drop'].forEach(eventName => { |
| | document.getElementById('dropzone').addEventListener(eventName, unhighlight, false); |
| | }); |
| | |
| | function highlight() { |
| | document.getElementById('dropzone').classList.add('dropzone-active'); |
| | } |
| | |
| | function unhighlight() { |
| | document.getElementById('dropzone').classList.remove('dropzone-active'); |
| | } |
| | |
| | document.getElementById('dropzone').addEventListener('drop', handleDrop, false); |
| | |
| | function handleDrop(e) { |
| | const dt = e.dataTransfer; |
| | const files = dt.files; |
| | document.getElementById('image-upload').files = files; |
| | |
| | |
| | const event = new Event('change'); |
| | document.getElementById('image-upload').dispatchEvent(event); |
| | } |
| | </script> |
| | </body> |
| | </html> |
| |
|