// ── Avatar Editor Modal ── // Wiederverwendbar für Session + Agent Avatar-Bearbeitung. // // Usage: // const editor = AvatarEditor.init({ // previewEl: document.getElementById('myPreview'), // initialImage: 'data:image/...' || null, // initialScale: 1.5, // onChange(imageData, scale) { ... } // }); // // Cleanup: // editor.destroy(); // // Get current values: // editor.getImage(); // data-url or null // editor.getScale(); // number var AvatarEditor = (function () { function init(opts) { var previewEl = opts.previewEl; var imageData = opts.initialImage || null; var scale = opts.initialScale || 1; var onChange = opts.onChange || function () {}; var overlay = null; var pasteHandler = null; var kreaAvailable = false; // Krea-Status prüfen apiKreaStatus().then(function (s) { kreaAvailable = s.configured; }).catch(function () {}); // Preview rendern function renderPreview() { if (imageData) { previewEl.innerHTML = ''; previewEl.classList.add('has-image'); } else { previewEl.innerHTML = '?'; previewEl.classList.remove('has-image'); } } renderPreview(); // Click → Modal öffnen previewEl.style.cursor = 'pointer'; previewEl.addEventListener('click', openModal); // Paste Handler (global, immer aktiv) document.addEventListener('paste', pasteHandler = function (e) { if (overlay) return; // Modal offen → Paste dort behandeln var items = e.clipboardData && e.clipboardData.items; if (!items) return; for (var i = 0; i < items.length; i++) { if (items[i].type.indexOf('image/') === 0) { e.preventDefault(); readFile(items[i].getAsFile()); return; } } }); function readFile(file) { if (!file) return; var reader = new FileReader(); reader.onload = function () { imageData = reader.result; renderPreview(); onChange(imageData, scale); if (overlay) refreshModal(); }; reader.readAsDataURL(file); } function setImage(data) { imageData = data; scale = 1; renderPreview(); onChange(imageData, scale); } function openModal() { if (overlay) return; overlay = document.createElement('div'); overlay.className = 'av-overlay'; overlay.innerHTML = '
' + '
' + '

Avatar bearbeiten

' + '' + '
' + '
' + '
' + (imageData ? '' : '?') + '
' + '
' + '
' + '' + '' + '' + scale + '×' + '
' + '
' + '' + '' + '' + '
' + '
' + '
' + '' + '' + '
' + '
' + '
' + '
' + '
'; document.body.appendChild(overlay); // Refs var modal = overlay.querySelector('.av-modal'); var closeBtn = overlay.querySelector('.av-close'); var previewLarge = overlay.querySelector('.av-preview-large'); var scaleRow = overlay.querySelector('.av-scale-row'); var scaleInput = overlay.querySelector('.av-scale'); var scaleValueEl = overlay.querySelector('.av-scale-value'); var uploadBtn = overlay.querySelector('.av-upload-btn'); var fileInput = overlay.querySelector('.av-file-input'); var clearBtn = overlay.querySelector('.av-clear-btn'); var aiPrompt = overlay.querySelector('.av-ai-prompt'); var aiBtn = overlay.querySelector('.av-ai-btn'); // Close closeBtn.addEventListener('click', closeModal); overlay.addEventListener('click', function (e) { if (e.target === overlay) closeModal(); }); // Scale scaleInput.addEventListener('input', function () { scale = parseFloat(scaleInput.value); scaleValueEl.textContent = scale + '×'; var img = previewLarge.querySelector('img'); if (img) img.style.scale = scale; renderPreview(); onChange(imageData, scale); }); // Upload uploadBtn.addEventListener('click', function () { fileInput.click(); }); fileInput.addEventListener('change', function () { if (fileInput.files[0]) readFile(fileInput.files[0]); }); // Clear clearBtn.addEventListener('click', function () { imageData = null; scale = 1; scaleInput.value = 1; scaleValueEl.textContent = '1×'; renderPreview(); onChange(imageData, scale); refreshModal(); }); // AI Generate aiBtn.addEventListener('click', generateAi); aiPrompt.addEventListener('keydown', function (e) { if (e.key === 'Enter') { e.preventDefault(); generateAi(); } }); async function generateAi() { var prompt = aiPrompt.value.trim(); if (!prompt) return; aiBtn.disabled = true; aiBtn.innerHTML = ''; try { var result = await apiKreaGenerate(prompt); if (result.files && result.files.length > 0) { var imgResp = await fetch('/api/images/file/' + result.files[0]); var blob = await imgResp.blob(); var reader = new FileReader(); reader.onload = function () { imageData = reader.result; scale = 1; scaleInput.value = 1; scaleValueEl.textContent = '1×'; renderPreview(); onChange(imageData, scale); refreshModal(); }; reader.readAsDataURL(blob); } } catch (e) { showToast('Fehler: ' + e.message, 'error'); } finally { aiBtn.disabled = false; aiBtn.innerHTML = ''; } } // Paste im Modal modal.addEventListener('paste', function (e) { var items = e.clipboardData && e.clipboardData.items; if (!items) return; for (var i = 0; i < items.length; i++) { if (items[i].type.indexOf('image/') === 0) { e.preventDefault(); readFile(items[i].getAsFile()); return; } } }); // Escape overlay._keyHandler = function (e) { if (e.key === 'Escape') closeModal(); }; document.addEventListener('keydown', overlay._keyHandler); } function refreshModal() { if (!overlay) return; var previewLarge = overlay.querySelector('.av-preview-large'); var scaleRow = overlay.querySelector('.av-scale-row'); var clearBtn = overlay.querySelector('.av-clear-btn'); if (imageData) { previewLarge.innerHTML = ''; scaleRow.hidden = false; clearBtn.hidden = false; } else { previewLarge.innerHTML = '?'; scaleRow.hidden = true; clearBtn.hidden = true; } } function closeModal() { if (!overlay) return; document.removeEventListener('keydown', overlay._keyHandler); overlay.remove(); overlay = null; } // Public API return { getImage: function () { return imageData; }, getScale: function () { return scale; }, destroy: function () { closeModal(); if (pasteHandler) { document.removeEventListener('paste', pasteHandler); pasteHandler = null; } previewEl.removeEventListener('click', openModal); previewEl.style.cursor = ''; } }; } return { init: init }; })();