/* ========================================================= BASYX V11 UI ENGINE Production-grade frontend runtime ========================================================= */ const API_BASE = ""; /* ========================================================= STATE ========================================================= */ const state = { currentTask: "transcribe", loading: false, }; /* ========================================================= SAFE ELEMENT ACCESS ========================================================= */ function el(id) { const node = document.getElementById(id); if (!node) console.warn(`[UI] Missing element: ${id}`); return node; } /* ========================================================= NAVIGATION SYSTEM ========================================================= */ function nav(task) { state.currentTask = task; document.querySelectorAll(".section").forEach((s) => s.classList.remove("active") ); const page = el(task); if (page) page.classList.add("active"); document.querySelectorAll(".nav-item").forEach((n) => n.classList.remove("active") ); if (event && event.target) { event.target.classList.add("active"); } location.hash = task; } /* Restore on reload */ window.addEventListener("load", () => { const hash = location.hash.replace("#", ""); if (hash && el(hash)) nav(hash); }); /* ========================================================= UI LOADING STATE ========================================================= */ function setLoading(task, isLoading) { const btn = el(`btn-${task}`); if (!btn) return; btn.disabled = isLoading; btn.innerText = isLoading ? "Processing..." : "Execute"; } /* ========================================================= ERROR HANDLING ========================================================= */ async function safeFetch(url, options, retries = 2) { try { const res = await fetch(url, options); if (!res.ok) { const text = await res.text(); throw new Error(`HTTP ${res.status}: ${text}`); } return res; } catch (err) { if (retries > 0) { await new Promise((r) => setTimeout(r, 800)); return safeFetch(url, options, retries - 1); } throw err; } } /* ========================================================= RESPONSE PARSER (JSON / BLOB / VIDEO) ========================================================= */ async function parseResponse(res, task) { const contentType = res.headers.get("content-type") || ""; // VIDEO OR FILE OUTPUT if ( contentType.includes("video") || contentType.includes("octet-stream") ) { const blob = await res.blob(); return { type: "blob", url: URL.createObjectURL(blob), }; } // JSON OUTPUT const data = await res.json(); return { type: "json", data, }; } /* ========================================================= INPUT COLLECTION ========================================================= */ function collectInput(task) { const file = el(`file-${task}`)?.files?.[0]; const url = el(`url-${task}`)?.value?.trim(); const fd = new FormData(); if (file) fd.append("file", file); if (url) fd.append("url_input", url); return fd; } /* ========================================================= CORE EXECUTION ========================================================= */ async function run(task) { if (state.loading) return; state.loading = true; setLoading(task, true); try { const fd = collectInput(task); const res = await safeFetch(`${API_BASE}/execute/${task}`, { method: "POST", body: fd, }); const parsed = await parseResponse(res, task); renderOutput(task, parsed); } catch (err) { console.error(err); renderError(task, err.message); } finally { state.loading = false; setLoading(task, false); } } /* ========================================================= OUTPUT RENDERING ========================================================= */ function renderOutput(task, result) { const outputBox = el(`result-${task}`); const videoBox = el(`video-${task}`); if (result.type === "blob") { if (videoBox) { videoBox.src = result.url; videoBox.style.display = "block"; } return; } if (outputBox) { outputBox.textContent = JSON.stringify(result.data, null, 2); } } /* ========================================================= ERROR UI ========================================================= */ function renderError(task, message) { const outputBox = el(`result-${task}`); if (outputBox) { outputBox.textContent = `ERROR:\n${message}`; outputBox.style.color = "#ff4d4d"; } } /* ========================================================= HEALTH CHECK (optional future UI use) ========================================================= */ async function checkHealth() { try { const res = await fetch("/health"); return await res.json(); } catch { return { status: "offline" }; } } /* ========================================================= MOBILE SIDEBAR TOGGLE ========================================================= */ function toggleSidebar() { const sidebar = document.querySelector(".sidebar"); if (!sidebar) return; sidebar.classList.toggle("open"); } /* Close sidebar on nav click (mobile UX) */ document.addEventListener("click", (e) => { if (e.target.classList.contains("nav-item")) { const sidebar = document.querySelector(".sidebar"); if (sidebar) sidebar.classList.remove("open"); } });