Spaces:
Paused
Paused
File size: 5,973 Bytes
8b950cf | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | // 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();
}
}
|