Spaces:
Running
Running
| /* ========================================================= | |
| 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"); | |
| } | |
| }); |