// Wires the ".alog reference curve" panel: local file upload, and browsing a server-side // library directory (e.g. wherever the roastetta skill already downloaded logs). export function initAlogPanel({ state, recompute }) { const fileInput = document.getElementById("alog-file"); const libraryBtn = document.getElementById("alog-library-refresh"); const libraryList = document.getElementById("alog-library-list"); const resultEl = document.getElementById("alog-result"); fileInput.addEventListener("change", async (e) => { const file = e.target.files?.[0]; if (!file) return; resultEl.innerHTML = "

Parsing…

"; try { const content = await file.text(); const res = await fetch("/api/alog", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ filename: file.name, content }), }); const body = await res.json(); applyResult(body); } catch (err) { resultEl.innerHTML = `

${escapeHtml(err.message)}

`; } }); libraryBtn.addEventListener("click", async () => { libraryList.classList.remove("hidden"); libraryList.innerHTML = "
  • Loading…
  • "; try { const res = await fetch("/api/alog/library"); const body = await res.json(); if (!body.ok || body.files.length === 0) { libraryList.innerHTML = "
  • No .alog files found. Set ALOG_DIR or drop files in ~/Roastetta.
  • "; return; } libraryList.innerHTML = ""; for (const f of body.files) { const li = document.createElement("li"); li.textContent = `${f.filename} (${Math.round(f.sizeBytes / 1024)} KB)`; li.addEventListener("click", async () => { resultEl.innerHTML = "

    Loading…

    "; const r = await fetch(`/api/alog/library/${encodeURIComponent(f.filename)}`); applyResult(await r.json()); }); libraryList.appendChild(li); } } catch (err) { libraryList.innerHTML = `
  • ${escapeHtml(err.message)}
  • `; } }); function applyResult(body) { if (!body.ok) { resultEl.innerHTML = `

    ${escapeHtml(body.error ?? body.code)}

    `; return; } state.plan.reference = body; recompute(); const warnings = (body.warnings ?? []).map((w) => `
  • ${escapeHtml(w)}
  • `).join(""); resultEl.innerHTML = `

    ${escapeHtml(body.roast.title)} — ${body.roast.roastDate || "no date"} · first crack ${fmt(body.derived?.firstCrackS)} · development ${fmt(body.derived?.developmentS)} · drop ${fmt(body.derived?.dropS)} · DTR ${body.derived?.dtrPct ?? "—"}%

    ${warnings ? `` : ""} `; } } function fmt(seconds) { if (seconds === null || seconds === undefined) return "—"; const s = Math.round(seconds); return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, "0")}`; } function escapeHtml(s) { return String(s ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c])); }