Spaces:
Running
Running
| ```javascript | |
| class ImageProcessor extends HTMLElement { | |
| constructor() { | |
| super(); | |
| this.selectedModel = 'prompthero/openjourney'; | |
| this.uploadedImage = null; | |
| } | |
| connectedCallback() { | |
| this.render(); | |
| this.attachEventListeners(); | |
| } | |
| render() { | |
| this.innerHTML = ` | |
| <style> | |
| .processor-container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| padding: 20px; | |
| } | |
| .model-selector { | |
| display: flex; | |
| gap: 15px; | |
| margin-bottom: 30px; | |
| flex-wrap: wrap; | |
| } | |
| .model-option { | |
| padding: 15px; | |
| border: 2px solid #e5e7eb; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| flex: 1; | |
| min-width: 200px; | |
| text-align: center; | |
| transition: all 0.3s; | |
| } | |
| .model-option:hover { | |
| border-color: #3b82f6; | |
| } | |
| .model-option.active { | |
| border-color: #3b82f6; | |
| background-color: #dbeafe; | |
| } | |
| .upload-area { | |
| border: 2px dashed #d1d5db; | |
| border-radius: 8px; | |
| padding: 40px; | |
| text-align: center; | |
| margin-bottom: 30px; | |
| position: relative; | |
| } | |
| .upload-area.drag-over { | |
| border-color: #3b82f6; | |
| background-color: #eff6ff; | |
| } | |
| .preview-container { | |
| display: flex; | |
| gap: 30px; | |
| flex-wrap: wrap; | |
| } | |
| .image-preview { | |
| flex: 1; | |
| min-width: 300px; | |
| } | |
| .preview-image { | |
| max-width: 100%; | |
| max-height: 400px; | |
| display: block; | |
| margin: 0 auto; | |
| border-radius: 8px; | |
| } | |
| .controls { | |
| margin: 20px 0; | |
| } | |
| .prompt-input { | |
| width: 100%; | |
| padding: 12px; | |
| border: 1px solid #d1d5db; | |
| border-radius: 6px; | |
| margin-bottom: 15px; | |
| } | |
| .process-btn { | |
| background-color: #10b981; | |
| color: white; | |
| border: none; | |
| padding: 12px 24px; | |
| border-radius: 6px; | |
| cursor: pointer; | |
| font-weight: bold; | |
| width: 100%; | |
| } | |
| .process-btn:hover:not(:disabled) { | |
| background-color: #059669; | |
| } | |
| .process-btn:disabled { | |
| background-color: #9ca3af; | |
| cursor: not-allowed; | |
| } | |
| .result-container { | |
| position: relative; | |
| min-height: 400px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .processing-overlay { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background-color: rgba(255, 255, 255, 0.8); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| flex-direction: column; | |
| z-index: 10; | |
| } | |
| .spinner { | |
| border: 4px solid #f3f3f3; | |
| border-top: 4px solid #3b82f6; | |
| border-radius: 50%; | |
| width: 40px; | |
| height: 40px; | |
| animation: spin 1s linear infinite; | |
| margin-bottom: 15px; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| .action-buttons { | |
| display: flex; | |
| gap: 15px; | |
| margin-top: 20px; | |
| } | |
| .action-btn { | |
| flex: 1; | |
| padding: 12px; | |
| border: none; | |
| border-radius: 6px; | |
| font-weight: bold; | |
| cursor: pointer; | |
| } | |
| .download-btn { | |
| background-color: #3b82f6; | |
| color: white; | |
| } | |
| .save-btn { | |
| background-color: #ef4444; | |
| color: white; | |
| } | |
| .action-btn:disabled { | |
| background-color: #9ca3af; | |
| cursor: not-allowed; | |
| } | |
| </style> | |
| <div class="processor-container"> | |
| <h2>AI Image Processor</h2> | |
| <div class="model-selector"> | |
| <div class="model-option active" data-model="prompthero/openjourney"> | |
| <h3>Artistic Style Transfer</h3> | |
| <p>Apply artistic styles to your images</p> | |
| </div> | |
| <div class="model-option" data-model="stabilityai/stable-diffusion-x4-upscaler"> | |
| <h3>Super Resolution</h3> | |
| <p>Enhance image resolution and details</p> | |
| </div> | |
| <div class="model-option" data-model="lllyasviel/sd-controlnet-canny"> | |
| <h3>Edge Enhancement</h3> | |
| <p>Transform images using edge detection</p> | |
| </div> | |
| </div> | |
| <div class="upload-area" id="drop-zone"> | |
| <p>Drag & drop your image here or click to browse</p> | |
| <input type="file" id="file-input" accept="image/*" style="display: none;"> | |
| <button id="browse-btn">Select Image</button> | |
| <img id="preview" class="preview-image" style="display: none; margin-top: 20px;"> | |
| </div> | |
| <div class="preview-container"> | |
| <div class="image-preview"> | |
| <h3>Input Image</h3> | |
| <div id="input-preview" class="result-container"> | |
| <p>No image selected</p> | |
| </div> | |
| </div> | |
| <div class="image-preview"> | |
| <h3>Processed Result</h3> | |
| <div id="output-preview" class="result-container"> | |
| <p>Processed image will appear here</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| } | |
| attachEventListeners() { | |
| // Model selection | |
| const modelOptions = this.querySelectorAll('.model-option'); | |
| modelOptions.forEach(option => { | |
| option.addEventListener('click', (e) => { | |
| modelOptions.forEach(opt => opt.classList.remove('active')); | |
| e.currentTarget.classList.add('active'); | |
| this.selectedModel = e.currentTarget.dataset.model; | |
| }); | |
| }); | |
| // File upload | |
| const dropZone = this.querySelector('#drop-zone'); | |
| const fileInput = this.querySelector('#file-input'); | |
| const browseBtn = this.querySelector('#browse-btn'); | |
| const preview = this.querySelector('#preview'); | |
| browseBtn.addEventListener('click', () => fileInput.click()); | |
| fileInput.addEventListener('change', (e) => { | |
| if (e.target.files.length) { | |
| this.handleFile(e.target.files[0]); | |
| } | |
| }); | |
| // Drag and drop events | |
| dropZone.addEventListener('dragover', (e) => { | |
| e.preventDefault(); | |
| dropZone.classList.add('drag-over'); | |
| }); | |
| dropZone.addEventListener('dragleave', () => { | |
| dropZone.classList.remove('drag-over'); | |
| }); | |
| dropZone.addEventListener('drop', (e) => { | |
| e.preventDefault(); | |
| dropZone.classList.remove('drag-over'); | |
| if (e.dataTransfer.files.length) { | |
| this.handleFile(e.dataTransfer.files[0]); | |
| } | |
| }); | |
| } | |
| handleFile(file) { | |
| if (!file.type.match('image.*')) { | |
| alert('Please select an image file'); | |
| return; | |
| } | |
| const reader = new FileReader(); | |
| reader.onload = (e) => { | |
| this.uploadedImage = e.target.result; | |
| this.updatePreview(); | |
| }; | |
| reader.readAsDataURL(file); | |
| } | |
| updatePreview() { | |
| const inputPreview = this.querySelector('#input-preview'); | |
| inputPreview.innerHTML = `<img src="${this.uploadedImage}" class="preview-image">`; | |
| } | |
| async processImage(prompt) { | |
| if (!this.uploadedImage) { | |
| alert('Please upload an image first'); | |
| return; | |
| } | |
| const outputPreview = this.querySelector('#output-preview'); | |
| outputPreview.innerHTML = ` | |
| <div class="processing-overlay"> | |
| <div class="spinner"></div> | |
| <p>Processing image...</p> | |
| </div> | |
| <img src="${this.uploadedImage}" class="preview-image" style="opacity: 0.3;"> | |
| `; | |
| try { | |
| // Convert base64 to blob | |
| const imageData = this.uploadedImage.split(',')[1]; | |
| const byteString = atob(imageData); | |
| const mimeString = this.uploadedImage.split(',')[0].split(':')[1].split(';')[0]; | |
| const ab = new ArrayBuffer(byteString.length); | |
| const ia = new Uint8Array(ab); | |
| for (let i = 0; i < byteString.length; i++) { | |
| ia[i] = byteString.charCodeAt(i); | |
| } | |
| const blob = new Blob([ab], {type: mimeString}); | |
| // Prepare form data | |
| const formData = new FormData(); | |
| formData.append('inputs', prompt); | |
| formData.append('image', blob, 'input.png'); | |
| // Call Hugging Face API | |
| const response = await fetch( | |
| `https://api-inference.huggingface.co/models/${this.selectedModel}`, | |
| { | |
| headers: { | |
| 'Authorization': 'Bearer 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' | |
| }, | |
| method: 'POST', | |
| body: formData | |
| } | |
| ); | |
| if (!response.ok) { | |
| throw new Error(`API error: ${response.status}`); | |
| } | |
| const blobResult = await response.blob(); | |
| const imageUrl = URL.createObjectURL(blobResult); | |
| // Display result | |
| outputPreview.innerHTML = ` | |
| <img src="${imageUrl}" class="preview-image"> | |
| <div class="action-buttons"> | |
| <button class="action-btn download-btn" onclick="this.downloadImage('${imageUrl}')">Download</button> | |
| <button class="action-btn save-btn">Save to Gallery</button> | |
| </div> | |
| `; | |
| } catch (error) { | |
| console.error('Error processing image:', error); | |
| outputPreview.innerHTML = ` | |
| <p>Error processing image. Please try again.</p> | |
| <img src="${this.uploadedImage}" class="preview-image" style="opacity: 0.3;"> | |
| `; | |
| } | |
| } | |
| downloadImage(url) { | |
| const link = document.createElement('a'); | |
| link.href = url; | |
| link.download = 'processed-image.png'; | |
| document.body.appendChild(link); | |
| link.click(); | |
| document.body.removeChild(link); | |
| } | |
| } | |
| customElements.define('image-processor', ImageProcessor); | |
| >>>>>>> REPLACE |