deedrop1140's picture
Update static/script.js
f6c4ff7 verified
// ---------- Glowing cursor effect ----------
const glowingEffect = document.createElement('div');
glowingEffect.classList.add('glowing-effect', 'js-generated');
document.body.appendChild(glowingEffect);
// Update CSS custom properties for the glow position (mouse + touch)
function updateGlow(x, y) {
glowingEffect.style.setProperty('--x', `${x}px`);
glowingEffect.style.setProperty('--y', `${y}px`);
}
document.addEventListener('mousemove', (e) => updateGlow(e.clientX, e.clientY));
document.addEventListener('touchmove', (e) => {
if (e.touches && e.touches[0]) {
updateGlow(e.touches[0].clientX, e.touches[0].clientY);
}
}, { passive: true });
// ---------- Helper utilities ----------
const qs = (id) => document.getElementById(id);
const showOutput = (html) => {
const outputDiv = qs('output') || (() => {
const d = document.createElement('div');
d.id = 'output';
document.body.appendChild(d);
return d;
})();
outputDiv.innerHTML = html;
};
const createDownloadLink = (url, label = 'Download') => {
const a = document.createElement('a');
a.href = url;
a.textContent = label;
a.download = '';
a.className = 'download-link';
return a;
};
const createImgPreview = (src, alt = 'preview') => {
const img = document.createElement('img');
img.src = src;
img.alt = alt;
img.style.maxWidth = '320px';
img.style.height = 'auto';
img.style.display = 'block';
img.style.marginTop = '12px';
img.style.borderRadius = '8px';
return img;
};
// ---------- Elements (gracefully handle missing elements) ----------
const imageInput = qs('image') || qs('image-input'); // caption image
const foregroundInput = qs('foreground'); // foreground for change bg / remove bg
const backgroundInput = qs('background'); // background for change bg (optional)
const generateCaptionBtn = qs('generate-caption-btn');
const removeBackgroundBtn = qs('remove-background-btn');
const changeBackgroundBtn = qs('change-background-btn');
// ---------- Core request functions using FormData ----------
async function postFormData(url, formData) {
const res = await fetch(url, {
method: 'POST',
body: formData, // do NOT set Content-Type; browser will add the correct boundary
});
if (!res.ok) {
const text = await res.text().catch(() => '');
throw new Error(`Server responded ${res.status}: ${text}`);
}
return res.json().catch(() => { throw new Error('Invalid JSON response'); });
}
// ---------- Generate Caption ----------
if (generateCaptionBtn) {
generateCaptionBtn.addEventListener('click', async (e) => {
e.preventDefault();
try {
const fileInput = imageInput;
if (!fileInput || !fileInput.files || fileInput.files.length === 0) {
showOutput('<span style="color:#b00">Please choose an image to generate caption.</span>');
return;
}
showOutput('Generating caption…');
const fd = new FormData();
fd.append('image', fileInput.files[0]);
const data = await postFormData('/generate_caption', fd);
// Expecting JSON like: { caption: "...", image_url: "..." } but handle flexibly
const caption = data.caption || data.text || '';
const imageUrl = data.image_url || data.result_url || data.output_url || null;
let html = `<strong>Generated Caption:</strong><div style="margin-top:8px">${caption || '<em>(no caption returned)</em>'}</div>`;
if (imageUrl) {
const img = createImgPreview(imageUrl, 'Generated caption preview');
html += '<div></div>'; // separator
// we will set outputDiv innerHTML, then append image node and download link
}
showOutput(html);
if (imageUrl) {
const outDiv = qs('output');
outDiv.appendChild(createImgPreview(imageUrl, 'Generated caption preview'));
outDiv.appendChild(createDownloadLink(imageUrl, 'Download Result Image'));
// Optional: add "Copy caption" button
if (caption) {
const copyBtn = document.createElement('button');
copyBtn.textContent = 'Copy Caption';
copyBtn.style.marginLeft = '8px';
copyBtn.addEventListener('click', () => {
navigator.clipboard?.writeText(caption).then(() => {
copyBtn.textContent = 'Copied!';
setTimeout(() => copyBtn.textContent = 'Copy Caption', 1500);
}).catch(() => {
copyBtn.textContent = 'Copy failed';
});
});
outDiv.appendChild(copyBtn);
}
}
} catch (err) {
console.error(err);
showOutput(`<span style="color:#b00">Error generating caption: ${err.message}</span>`);
}
});
}
// ---------- Remove Background ----------
if (removeBackgroundBtn) {
removeBackgroundBtn.addEventListener('click', async (e) => {
e.preventDefault();
try {
// Prefer explicit foreground input; fallback to generic image input
const fileInput = foregroundInput || imageInput;
if (!fileInput || !fileInput.files || fileInput.files.length === 0) {
showOutput('<span style="color:#b00">Please choose an image to remove background.</span>');
return;
}
showOutput('Removing background…');
const fd = new FormData();
fd.append('image', fileInput.files[0]);
const data = await postFormData('/remove_background', fd);
// Expect JSON like: { output_url: "..." } or { output_path: "..." }
const outUrl = data.output_url || data.output_path || data.result_url || null;
if (outUrl) {
const outHtml = '<strong>Removed Background:</strong>';
showOutput(outHtml);
const outDiv = qs('output');
outDiv.appendChild(createImgPreview(outUrl, 'Result - background removed'));
outDiv.appendChild(createDownloadLink(outUrl, 'Download Result Image'));
} else {
showOutput('<span style="color:#b00">Background removed but server did not return a URL.</span>');
}
} catch (err) {
console.error(err);
showOutput(`<span style="color:#b00">Error removing background: ${err.message}</span>`);
}
});
}
// ---------- Change Background ----------
if (changeBackgroundBtn) {
changeBackgroundBtn.addEventListener('click', async (e) => {
e.preventDefault();
try {
const fgInput = foregroundInput || imageInput;
if (!fgInput || !fgInput.files || fgInput.files.length === 0) {
showOutput('<span style="color:#b00">Please choose a foreground image.</span>');
return;
}
// Option A: use a second file input for background if available
// Option B: fallback to a hard-coded URL (if your server supports that)
const bgFile = (backgroundInput && backgroundInput.files && backgroundInput.files[0]) || null;
const FALLBACK_BACKGROUND_URL = null; // set a URL string here only if your server supports background URLs
if (!bgFile && !FALLBACK_BACKGROUND_URL) {
showOutput('<span style="color:#b00">Please choose a background image (or configure FALLBACK_BACKGROUND_URL in the script).</span>');
return;
}
showOutput('Changing background…');
const fd = new FormData();
fd.append('foreground', fgInput.files[0]);
if (bgFile) {
fd.append('background', bgFile);
} else {
fd.append('background_url', FALLBACK_BACKGROUND_URL);
}
const data = await postFormData('/change_background', fd);
const outUrl = data.output_url || data.output_path || data.result_url || null;
if (outUrl) {
showOutput('<strong>Changed Background:</strong>');
const outDiv = qs('output');
outDiv.appendChild(createImgPreview(outUrl, 'Result - background changed'));
outDiv.appendChild(createDownloadLink(outUrl, 'Download Result Image'));
} else {
showOutput('<span style="color:#b00">Background changed but server did not return a URL.</span>');
}
} catch (err) {
console.error(err);
showOutput(`<span style="color:#b00">Error changing background: ${err.message}</span>`);
}
});
}
// ---------- Accessibility: enable Enter key on focused file inputs when needed ----------
document.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
// try to trigger caption generation if that button exists and is visible
const active = document.activeElement;
if (active && (active.tagName === 'INPUT' && active.type === 'file')) {
// don't auto-submit; just do nothing (files require user action)
return;
}
}
});
// ---------- Optional: add minimal CSS for the glowing effect if not already in CSS ----------
(function ensureGlowCss() {
const id = 'glow-css';
if (document.getElementById(id)) return;
const style = document.createElement('style');
style.id = id;
style.textContent = `
.glowing-effect {
position: fixed;
left: 0;
top: 0;
width: 240px;
height: 240px;
pointer-events: none;
transform: translate(calc(var(--x, 0) - 50%), calc(var(--y, 0) - 50%));
mix-blend-mode: screen;
filter: blur(40px);
z-index: 9999;
transition: transform 0.04s linear;
background: radial-gradient(circle at center, rgba(0,150,255,0.18) 0%, rgba(0,150,255,0.06) 40%, transparent 70%);
}
.glowing-effect.js-generated { opacity: 0.95; }
#output { margin: 14px auto; max-width: 720px; text-align: left; }
.download-link { display:inline-block; margin-top:8px; padding:8px 12px; background:#007bff; color:#fff; border-radius:6px; text-decoration:none; }
.download-link:hover { background:#0056b3; }
`;
document.head.appendChild(style);
})();