// Same-origin by default — the backend serves this frontend directly. // If you deploy the frontend separately from the backend, set this to the // backend's full URL instead, e.g. "https://your-backend.onrender.com" const API_BASE = ""; const tabs = document.querySelectorAll(".tab"); const panels = document.querySelectorAll(".panel"); const submitBtn = document.getElementById("submit-btn"); const statusEl = document.getElementById("status"); const statusText = document.getElementById("status-text"); const resultEl = document.getElementById("result"); const resultText = document.getElementById("result-text"); const resultAudio = document.getElementById("result-audio"); const downloadLink = document.getElementById("download-link"); const errorEl = document.getElementById("error"); const textInput = document.getElementById("text-input"); const textCount = document.getElementById("text-count"); let activeTab = "text"; tabs.forEach((tab) => { tab.addEventListener("click", () => { activeTab = tab.dataset.tab; tabs.forEach((t) => { t.classList.toggle("is-active", t === tab); t.setAttribute("aria-selected", t === tab ? "true" : "false"); }); panels.forEach((p) => { const isMatch = p.dataset.panel === activeTab; p.classList.toggle("is-active", isMatch); p.hidden = !isMatch; }); clearMessages(); }); }); textInput.addEventListener("input", () => { textCount.textContent = textInput.value.length; }); function clearMessages() { errorEl.hidden = true; resultEl.hidden = true; } function showError(message) { clearMessages(); errorEl.textContent = message; errorEl.hidden = false; } function getSelectedVoice() { return document.querySelector('input[name="voice"]:checked').value; } function setLoading(isLoading, label) { submitBtn.disabled = isLoading; statusEl.hidden = !isLoading; if (label) statusText.textContent = label; } async function handleResponse(resp) { if (!resp.ok) { let detail = `Request failed (${resp.status})`; try { const body = await resp.json(); detail = body.detail || detail; } catch (_) { /* response wasn't JSON, keep default message */ } throw new Error(detail); } const translatedText = resp.headers.get("X-Translated-Text") || (resp.headers.get("X-Sentence-Count") ? `${resp.headers.get("X-Sentence-Count")} sentences spoken` : ""); const blob = await resp.blob(); const url = URL.createObjectURL(blob); resultText.textContent = translatedText || "(no text preview available)"; resultAudio.src = url; downloadLink.href = url; resultEl.hidden = false; } submitBtn.addEventListener("click", async () => { clearMessages(); const voice = getSelectedVoice(); try { if (activeTab === "text") { const text = textInput.value.trim(); if (!text) return showError("Type some English text first."); setLoading(true, "Translating…"); const form = new FormData(); form.append("text", text); form.append("voice", voice); const resp = await fetch(`${API_BASE}/api/translate-speak`, { method: "POST", body: form }); await handleResponse(resp); } else if (activeTab === "audio") { const file = document.getElementById("audio-input").files[0]; if (!file) return showError("Choose an audio file first."); setLoading(true, "Transcribing and translating…"); const form = new FormData(); form.append("file", file); form.append("voice", voice); const resp = await fetch(`${API_BASE}/api/audio-translate`, { method: "POST", body: form }); await handleResponse(resp); } else if (activeTab === "video") { const file = document.getElementById("video-input").files[0]; if (!file) return showError("Choose a video file first."); setLoading(true, "Extracting audio and translating…"); const form = new FormData(); form.append("file", file); form.append("voice", voice); const resp = await fetch(`${API_BASE}/api/video-translate`, { method: "POST", body: form }); await handleResponse(resp); } else if (activeTab === "document") { const file = document.getElementById("document-input").files[0]; if (!file) return showError("Choose a document first."); const limit = document.getElementById("sentence-limit").value; setLoading(true, "Reading and translating document…"); const form = new FormData(); form.append("file", file); form.append("voice", voice); if (limit) form.append("max_sentences", limit); const resp = await fetch(`${API_BASE}/api/document-translate`, { method: "POST", body: form }); await handleResponse(resp); } } catch (err) { showError(err.message || "Something went wrong. Try again."); } finally { setLoading(false); } }); // Register the service worker for installability (best-effort; app still // works fine if this fails, e.g. on http:// during local development) if ("serviceWorker" in navigator) { window.addEventListener("load", () => { navigator.serviceWorker.register("sw.js").catch(() => {}); }); }