someone-in-the-world's picture
Fix Japanese IME exits on iPhone by checking e.isComposing
8a5d3bd
Raw
History Blame Contribute Delete
12 kB
() => {
function init() {
if (window.__fireRedInitDone) return;
const galleryGrid = document.getElementById('image-gallery-grid');
const dropZone = document.getElementById('gallery-drop-zone');
const uploadPrompt = document.getElementById('upload-prompt');
const uploadClick = document.getElementById('upload-click-area');
const fileInput = document.getElementById('custom-file-input');
const btnUpload = document.getElementById('tb-upload');
const btnRemove = document.getElementById('tb-remove');
const btnClear = document.getElementById('tb-clear');
const promptInput = document.getElementById('custom-prompt-input');
const runBtnEl = document.getElementById('custom-run-btn');
const imgCountTb = document.getElementById('tb-image-count');
const imgCountSb = document.getElementById('sb-image-count');
if (!galleryGrid || !fileInput || !dropZone) {
setTimeout(init, 250);
return;
}
window.__fireRedInitDone = true;
let images = [];
window.__uploadedImages = images;
let selectedIdx = -1;
let toastTimer = null;
function showToast(message, type) {
let toast = document.getElementById('app-toast');
if (!toast) {
toast = document.createElement('div');
toast.id = 'app-toast';
toast.className = 'toast-notification';
toast.innerHTML = '<span class="toast-icon"></span><span class="toast-text"></span>';
document.body.appendChild(toast);
}
const icon = toast.querySelector('.toast-icon');
const text = toast.querySelector('.toast-text');
toast.className = 'toast-notification ' + (type || 'error');
if (type === 'warning') icon.textContent = '⚠';
else if (type === 'info') icon.textContent = 'ℹ';
else icon.textContent = '✗';
text.textContent = message;
if (toastTimer) clearTimeout(toastTimer);
void toast.offsetWidth;
toast.classList.add('visible');
toastTimer = setTimeout(() => toast.classList.remove('visible'), 3500);
}
window.__showToast = showToast;
function flashPromptError() {
if (!promptInput) return;
promptInput.classList.add('error-flash');
promptInput.focus();
setTimeout(() => promptInput.classList.remove('error-flash'), 800);
}
function setGradioValue(containerId, value) {
const container = document.getElementById(containerId);
if (!container) return;
container.querySelectorAll('input, textarea').forEach(el => {
if (el.type === 'file' || el.type === 'range' || el.type === 'checkbox') return;
const proto = el.tagName === 'TEXTAREA' ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
const ns = Object.getOwnPropertyDescriptor(proto, 'value');
if (ns && ns.set) {
ns.set.call(el, value);
el.dispatchEvent(new Event('input', {bubbles:true, composed:true}));
el.dispatchEvent(new Event('change', {bubbles:true, composed:true}));
}
});
}
window.__setGradioValue = setGradioValue;
function syncImagesToGradio() {
window.__uploadedImages = images;
const b64Array = images.map(img => img.b64);
setGradioValue('hidden-images-b64', JSON.stringify(b64Array));
updateCounts();
}
function syncPromptToGradio() {
if (promptInput) setGradioValue('prompt-gradio-input', promptInput.value);
}
function updateCounts() {
const n = images.length;
const txt = n > 0 ? n + ' image' + (n > 1 ? 's' : '') : 'No images';
if (imgCountTb) imgCountTb.textContent = txt;
if (imgCountSb) imgCountSb.textContent = n > 0 ? txt + ' uploaded' : 'No images uploaded';
}
function addImage(b64, name) {
images.push({id: Date.now() + Math.random(), b64: b64, name: name});
renderGallery();
syncImagesToGradio();
}
window.__addImage = addImage;
function removeImage(idx) {
images.splice(idx, 1);
if (selectedIdx === idx) selectedIdx = -1;
else if (selectedIdx > idx) selectedIdx--;
renderGallery();
syncImagesToGradio();
}
function clearAll() {
images = [];
window.__uploadedImages = images;
selectedIdx = -1;
renderGallery();
syncImagesToGradio();
}
window.__clearAll = clearAll;
function selectImage(idx) {
selectedIdx = (selectedIdx === idx) ? -1 : idx;
renderGallery();
}
function renderGallery() {
if (images.length === 0) {
galleryGrid.innerHTML = '';
galleryGrid.style.display = 'none';
if (uploadPrompt) uploadPrompt.style.display = '';
return;
}
if (uploadPrompt) uploadPrompt.style.display = 'none';
galleryGrid.style.display = 'grid';
let html = '';
images.forEach((img, i) => {
const sel = i === selectedIdx ? ' selected' : '';
html += '<div class="gallery-thumb' + sel + '" data-idx="' + i + '">'
+ '<img src="' + img.b64 + '" alt="' + (img.name||'image') + '">'
+ '<span class="thumb-badge">#' + (i+1) + '</span>'
+ '<button class="thumb-remove" data-remove="' + i + '">✕</button>'
+ '</div>';
});
html += '<div class="gallery-add-card" id="gallery-add-card">'
+ '<span class="add-icon">+</span>'
+ '<span class="add-text">Add</span>'
+ '</div>';
galleryGrid.innerHTML = html;
galleryGrid.querySelectorAll('.gallery-thumb').forEach(thumb => {
thumb.addEventListener('click', (e) => {
if (e.target.closest('.thumb-remove')) return;
selectImage(parseInt(thumb.dataset.idx));
});
});
galleryGrid.querySelectorAll('.thumb-remove').forEach(btn => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
removeImage(parseInt(btn.dataset.remove));
});
});
const addCard = document.getElementById('gallery-add-card');
if (addCard) addCard.addEventListener('click', () => fileInput.click());
}
function processFiles(files) {
Array.from(files).forEach(file => {
if (!file.type.startsWith('image/')) return;
const reader = new FileReader();
reader.onload = (e) => addImage(e.target.result, file.name);
reader.readAsDataURL(file);
});
}
fileInput.addEventListener('change', (e) => { processFiles(e.target.files); e.target.value = ''; });
if (uploadClick) uploadClick.addEventListener('click', () => fileInput.click());
if (btnUpload) btnUpload.addEventListener('click', () => fileInput.click());
if (btnRemove) btnRemove.addEventListener('click', () => {
if (selectedIdx >= 0 && selectedIdx < images.length) removeImage(selectedIdx);
});
if (btnClear) btnClear.addEventListener('click', clearAll);
dropZone.addEventListener('dragover', (e) => { e.preventDefault(); dropZone.classList.add('drag-over'); });
dropZone.addEventListener('dragleave', (e) => { e.preventDefault(); dropZone.classList.remove('drag-over'); });
dropZone.addEventListener('drop', (e) => {
e.preventDefault(); dropZone.classList.remove('drag-over');
if (e.dataTransfer.files.length) processFiles(e.dataTransfer.files);
});
if (promptInput) promptInput.addEventListener('input', (e) => { if (!e.isComposing) syncPromptToGradio(); });
window.__setPrompt = function(text) {
if (promptInput) { promptInput.value = text; syncPromptToGradio(); }
};
document.querySelectorAll('.example-card[data-idx]').forEach(card => {
card.addEventListener('click', () => {
const idx = card.getAttribute('data-idx');
document.querySelectorAll('.example-card.loading').forEach(c => c.classList.remove('loading'));
card.classList.add('loading');
showToast('Loading example...', 'info');
setGradioValue('example-result-data', '');
setGradioValue('example-idx-input', idx);
setTimeout(() => {
const btn = document.getElementById('example-load-btn');
if (btn) {
const b = btn.querySelector('button');
if (b) b.click(); else btn.click();
}
}, 150);
setTimeout(() => card.classList.remove('loading'), 12000);
});
});
function syncSlider(customId, gradioId) {
const slider = document.getElementById(customId);
const valSpan = document.getElementById(customId + '-val');
if (!slider) return;
slider.addEventListener('input', () => {
if (valSpan) valSpan.textContent = slider.value;
const container = document.getElementById(gradioId);
if (!container) return;
container.querySelectorAll('input[type="range"],input[type="number"]').forEach(el => {
const ns = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
if (ns && ns.set) {
ns.set.call(el, slider.value);
el.dispatchEvent(new Event('input', {bubbles:true, composed:true}));
el.dispatchEvent(new Event('change', {bubbles:true, composed:true}));
}
});
});
}
syncSlider('custom-seed', 'gradio-seed');
syncSlider('custom-guidance', 'gradio-guidance');
syncSlider('custom-steps', 'gradio-steps');
syncSlider('custom-gpu-duration', 'gradio-gpu-duration');
const randCheck = document.getElementById('custom-randomize');
if (randCheck) {
randCheck.addEventListener('change', () => {
const container = document.getElementById('gradio-randomize');
if (!container) return;
const cb = container.querySelector('input[type="checkbox"]');
if (cb && cb.checked !== randCheck.checked) cb.click();
});
}
function showLoader() {
const l = document.getElementById('output-loader');
if (l) l.classList.add('active');
const sb = document.querySelector('.sb-fixed');
if (sb) sb.textContent = 'Processing...';
}
function hideLoader() {
const l = document.getElementById('output-loader');
if (l) l.classList.remove('active');
const sb = document.querySelector('.sb-fixed');
if (sb) sb.textContent = 'Done';
}
window.__showLoader = showLoader;
window.__hideLoader = hideLoader;
function validateBeforeRun() {
const promptVal = promptInput ? promptInput.value.trim() : '';
const hasImages = images.length > 0;
if (!hasImages && !promptVal) { showToast('Please upload an image and enter a prompt', 'error'); flashPromptError(); return false; }
if (!hasImages) { showToast('Please upload at least one image', 'error'); return false; }
if (!promptVal) { showToast('Please enter an edit prompt', 'warning'); flashPromptError(); return false; }
return true;
}
window.__clickGradioRunBtn = function() {
if (!validateBeforeRun()) return;
syncPromptToGradio(); syncImagesToGradio(); showLoader();
setTimeout(() => {
const gradioBtn = document.getElementById('gradio-run-btn');
if (!gradioBtn) return;
const btn = gradioBtn.querySelector('button');
if (btn) btn.click(); else gradioBtn.click();
}, 200);
};
if (runBtnEl) runBtnEl.addEventListener('click', () => window.__clickGradioRunBtn());
renderGallery();
updateCounts();
}
init();
}