const sourceEl = document.querySelector("#source"); const targetEl = document.querySelector("#target"); const inputEl = document.querySelector("#input"); const translateBtn = document.querySelector("#translate"); const clearBtn = document.querySelector("#clearInput"); const statusEl = document.querySelector("#status"); const denelaOut = document.querySelector("#denelaOut"); const englishOut = document.querySelector("#englishOut"); const turkishOut = document.querySelector("#turkishOut"); const checkline = document.querySelector("#checkline"); const breakdownBody = document.querySelector("#breakdown"); const analysisEl = document.querySelector("#analysis"); const warningsEl = document.querySelector("#warnings"); const tokenCountEl = document.querySelector("#tokenCount"); const breakdownColumns = [ ["form", "Form"], ["pos", "Part"], ["meaning_en", "English"], ["meaning_tr", "Turkish"], ["status", "Status"] ]; function setStatus(text, state = "") { statusEl.className = `status ${state}`.trim(); statusEl.innerHTML = ""; const dot = document.createElement("span"); dot.className = "status-dot"; const label = document.createElement("span"); label.textContent = text; statusEl.append(dot, label); } async function loadStatus() { const response = await fetch("/api/status"); const status = await response.json(); const backend = status.local_model_available ? "local model" : status.translation_backend; const model = status.local_model_available ? "specialized translators" : [status.llm_provider, status.llm_model].filter(Boolean).join(" · "); const pieces = [backend, model, `${status.lexicon_entries} entries`].filter(Boolean); setStatus(pieces.join(" · "), "ready"); } function renderRows(rows) { breakdownBody.innerHTML = ""; tokenCountEl.textContent = `${rows?.length || 0} tokens`; if (!rows || !rows.length) { const tr = document.createElement("tr"); tr.className = "empty-row"; const td = document.createElement("td"); td.colSpan = 5; td.textContent = "No Denela tokens"; tr.appendChild(td); breakdownBody.appendChild(tr); return; } for (const row of rows) { const tr = document.createElement("tr"); for (const [key, label] of breakdownColumns) { const td = document.createElement("td"); td.dataset.label = label; td.textContent = row[key] || ""; tr.appendChild(td); } breakdownBody.appendChild(tr); } } function renderWarnings(result) { warningsEl.innerHTML = ""; const warnings = [ ...(result.warnings || []), ...((result.validation && result.validation.warnings) || []) ]; for (const warning of warnings) { const li = document.createElement("li"); li.textContent = warning; warningsEl.appendChild(li); } } function renderCheckline(result) { checkline.className = "checkline"; const confidence = Number(result.confidence); const hasConfidence = Number.isFinite(confidence) && confidence > 0; if (result.validation && result.validation.ok === false) { checkline.classList.add("review"); checkline.textContent = "Review needed"; return; } if (result.meaning_preserved === true) { checkline.textContent = hasConfidence ? `Meaning preserved · ${confidence.toFixed(2)}` : "Meaning preserved"; return; } if (result.meaning_preserved === false) { checkline.classList.add("review"); checkline.textContent = hasConfidence ? `Meaning needs review · ${confidence.toFixed(2)}` : "Meaning needs review"; return; } checkline.textContent = result.backend === "local_model" ? "Local model" : "Translated"; } function renderResult(result) { denelaOut.textContent = result.denela || ""; englishOut.textContent = result.english || ""; turkishOut.textContent = result.turkish || ""; analysisEl.textContent = result.analysis || ""; renderCheckline(result); renderRows(result.tokens || []); renderWarnings(result); } async function translate() { const text = inputEl.value.trim(); if (!text) return; translateBtn.disabled = true; translateBtn.textContent = "Translating"; checkline.className = "checkline"; checkline.textContent = "Working"; try { const response = await fetch("/api/translate", { method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({ text, source: sourceEl.value, target: targetEl.value }) }); if (!response.ok) { const errorText = await response.text(); throw new Error(errorText); } renderResult(await response.json()); } catch (error) { checkline.className = "checkline error"; checkline.textContent = "Translation failed"; analysisEl.textContent = error.message; renderRows([]); warningsEl.innerHTML = ""; } finally { translateBtn.disabled = false; translateBtn.textContent = "Translate"; } } async function copyTarget(targetId, button) { const target = document.querySelector(`#${targetId}`); const text = target?.textContent?.trim(); if (!text) return; try { await navigator.clipboard.writeText(text); const original = button.textContent; button.textContent = "Copied"; setTimeout(() => { button.textContent = original; }, 1100); } catch { button.textContent = "Copy failed"; setTimeout(() => { button.textContent = "Copy"; }, 1100); } } translateBtn.addEventListener("click", translate); clearBtn.addEventListener("click", () => { inputEl.value = ""; inputEl.focus(); }); document.querySelectorAll("[data-copy-target]").forEach((button) => { button.addEventListener("click", () => copyTarget(button.dataset.copyTarget, button)); }); inputEl.addEventListener("keydown", (event) => { if ((event.metaKey || event.ctrlKey) && event.key === "Enter") { translate(); } }); loadStatus().catch(() => { setStatus("Status unavailable", "error"); }); renderRows([]); translate();