Spaces:
Running
Running
Download src/views/chat/image-editor.js from Luca448/APP-Backend: direct link, hf CLI and curl.
- Browser
- Download file 5.97 kB
-
https://huggingface.co/spaces/Luca448/APP-Backend/resolve/main/src/views/chat/image-editor.js
- Command line
-
hf download hf://spaces/Luca448/APP-Backend/src/views/chat/image-editor.js
-
curl -L -o image-editor.js https://huggingface.co/spaces/Luca448/APP-Backend/resolve/main/src/views/chat/image-editor.js
5.97 kB
| // image-editor.js - Nano Banana / Pollinations UI overlay for Image Editing | |
| export function initImageEditor() { | |
| if (document.getElementById('image-editor-modal')) return; | |
| const modalHtml = ` | |
| <div id="image-editor-modal" style="display: none; position: fixed; inset: 0; z-index: 9999; background: rgba(0,0,0,0.8); backdrop-filter: blur(10px); flex-direction: column; align-items: center; justify-content: center; opacity: 0; transition: opacity 0.3s ease;"> | |
| <div style="position: absolute; top: 20px; right: 20px; z-index: 10;"> | |
| <button id="close-image-editor" style="background: rgba(255,255,255,0.2); border: none; width: 40px; height: 40px; border-radius: 20px; color: white; font-size: 20px; cursor: pointer; display: flex; align-items: center; justify-content: center;"> | |
| <svg viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2" fill="none"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg> | |
| </button> | |
| </div> | |
| <div style="width: 90%; max-width: 600px; display: flex; flex-direction: column; gap: 16px;"> | |
| <div style="position: relative; width: 100%; aspect-ratio: 1/1; background: #111; border-radius: 12px; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.5);"> | |
| <img id="editor-base-image" style="width: 100%; height: 100%; object-fit: contain; pointer-events: none;" /> | |
| <canvas id="editor-canvas" style="position: absolute; inset: 0; width: 100%; height: 100%; cursor: crosshair; touch-action: none;"></canvas> | |
| </div> | |
| <div style="display: flex; gap: 8px; background: rgba(255,255,255,0.05); padding: 8px; border-radius: 12px; border: 1px solid rgba(255,255,255,0.1);"> | |
| <input type="text" id="editor-prompt" placeholder="Was möchtest du ändern? (z.B. 'Mach den Himmel blauer')" style="flex: 1; background: transparent; border: none; color: white; padding: 12px; font-size: 16px; outline: none; min-width: 0;" /> | |
| <button id="editor-submit" style="background: white; color: black; border: none; padding: 0 24px; border-radius: 8px; font-weight: 600; cursor: pointer;">Ändern</button> | |
| </div> | |
| <div style="text-align: center; color: rgba(255,255,255,0.5); font-size: 12px;">Hinweis: Maskierung (Inpainting) erfordert Nano Banana 2 (Premium). Aktuell wird der reine Text-Prompt verarbeitet.</div> | |
| </div> | |
| </div> | |
| `; | |
| document.body.insertAdjacentHTML('beforeend', modalHtml); | |
| const modal = document.getElementById('image-editor-modal'); | |
| const closeBtn = document.getElementById('close-image-editor'); | |
| const canvas = document.getElementById('editor-canvas'); | |
| const promptInput = document.getElementById('editor-prompt'); | |
| const submitBtn = document.getElementById('editor-submit'); | |
| const baseImg = document.getElementById('editor-base-image'); | |
| let ctx = canvas.getContext('2d'); | |
| let isDrawing = false; | |
| let currentPromptContext = ''; | |
| function resizeCanvas() { | |
| canvas.width = canvas.offsetWidth; | |
| canvas.height = canvas.offsetHeight; | |
| ctx.lineCap = 'round'; | |
| ctx.lineJoin = 'round'; | |
| ctx.lineWidth = 30; | |
| ctx.strokeStyle = 'rgba(255, 50, 50, 0.5)'; | |
| } | |
| window.addEventListener('resize', () => { | |
| if (modal.style.display !== 'none') resizeCanvas(); | |
| }); | |
| closeBtn.onclick = () => { | |
| modal.style.opacity = '0'; | |
| setTimeout(() => modal.style.display = 'none', 300); | |
| }; | |
| // Drawing Logic | |
| const startDrawing = (e) => { | |
| isDrawing = true; | |
| draw(e); | |
| }; | |
| const stopDrawing = () => { | |
| isDrawing = false; | |
| ctx.beginPath(); | |
| }; | |
| const draw = (e) => { | |
| if (!isDrawing) return; | |
| e.preventDefault(); | |
| const rect = canvas.getBoundingClientRect(); | |
| const clientX = e.touches ? e.touches[0].clientX : e.clientX; | |
| const clientY = e.touches ? e.touches[0].clientY : e.clientY; | |
| const x = clientX - rect.left; | |
| const y = clientY - rect.top; | |
| ctx.lineTo(x, y); | |
| ctx.stroke(); | |
| ctx.beginPath(); | |
| ctx.moveTo(x, y); | |
| }; | |
| canvas.addEventListener('mousedown', startDrawing); | |
| canvas.addEventListener('mousemove', draw); | |
| canvas.addEventListener('mouseup', stopDrawing); | |
| canvas.addEventListener('mouseout', stopDrawing); | |
| canvas.addEventListener('touchstart', startDrawing, { passive: false }); | |
| canvas.addEventListener('touchmove', draw, { passive: false }); | |
| canvas.addEventListener('touchend', stopDrawing); | |
| submitBtn.onclick = () => { | |
| const editInstruction = promptInput.value.trim(); | |
| if (!editInstruction) return; | |
| // For Pollinations (Option A), we just append the edit to the prompt or overwrite it. | |
| // In a real API, we'd send baseImg.src and canvas.toDataURL() as a mask. | |
| const newPrompt = `${currentPromptContext}, modified: ${editInstruction}`; | |
| // Send a message into the chat conceptually | |
| const input = document.querySelector('#chat-input'); | |
| const sendBtn = document.querySelector('#send-button'); | |
| if (input && sendBtn) { | |
| input.value = `Bitte generiere das Bild mit der Änderung: "${editInstruction}". Neuer Prompt: ${newPrompt}`; | |
| sendBtn.click(); | |
| } | |
| closeBtn.click(); | |
| }; | |
| window.openImageEditor = (url, originalPromptEncoded) => { | |
| baseImg.src = url; | |
| currentPromptContext = decodeURIComponent(originalPromptEncoded); | |
| promptInput.value = ''; | |
| modal.style.display = 'flex'; | |
| // Force reflow | |
| void modal.offsetWidth; | |
| modal.style.opacity = '1'; | |
| setTimeout(() => { | |
| resizeCanvas(); | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| }, 100); | |
| }; | |
| } | |
| // Auto-init on load if not a module or manually called | |
| if (typeof document !== 'undefined') { | |
| document.addEventListener('DOMContentLoaded', initImageEditor); | |
| // Just in case it's loaded dynamically after DOMContentLoaded | |
| if (document.readyState === 'complete' || document.readyState === 'interactive') { | |
| initImageEditor(); | |
| } | |
| } | |