import { forActiveRig, isActiveRigDecode } from "./chunk-S57W63QN.js"; import { hostCore } from "./chunk-KL66PICH.js"; // src/plugins/wefax.ts var wefaxWindow = window; var wefaxDom = { status: document.getElementById("wefax-status"), liveView: document.getElementById("wefax-live-view"), historyView: document.getElementById("wefax-history-view"), liveContainer: document.getElementById("wefax-live-container"), liveInfo: document.getElementById("wefax-live-info"), liveCanvas: document.getElementById("wefax-live-canvas"), liveLatest: document.getElementById("wefax-live-latest"), historyList: document.getElementById("wefax-history-list"), historyCount: document.getElementById("wefax-history-count"), filterInput: document.getElementById("wefax-filter"), sortSelect: document.getElementById("wefax-sort"), toggleBtn: document.getElementById("wefax-decode-toggle-btn"), clearBtn: document.getElementById("wefax-clear-btn"), viewLiveBtn: document.getElementById("wefax-view-live"), viewHistoryBtn: document.getElementById("wefax-view-history") }; var wefaxImageHistory = []; var WEFAX_MAX_IMAGES = 100; var wefaxLiveCtx = null; var wefaxLiveLineCount = 0; var wefaxLivePixelsPerLine = 1809; var wefaxActiveView = "live"; var wefaxFilterText = ""; function currentWefaxHistoryRetentionMs() { return wefaxWindow.getDecodeHistoryRetentionMs?.() ?? 24 * 60 * 60 * 1e3; } function pruneWefaxHistory() { const cutoff = Date.now() - currentWefaxHistoryRetentionMs(); wefaxImageHistory = wefaxImageHistory.filter(function(m) { return (m._tsMs || 0) > cutoff; }); } function escapeHtml(s) { return String(s).replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); } function scheduleWefaxUi(key, job) { if (typeof wefaxWindow.trxScheduleUiFrameJob === "function") { wefaxWindow.trxScheduleUiFrameJob(key, job); return; } job(); } function switchWefaxView(view) { wefaxActiveView = view; if (wefaxDom.liveView) wefaxDom.liveView.style.display = view === "live" ? "" : "none"; if (wefaxDom.historyView) wefaxDom.historyView.style.display = view === "history" ? "" : "none"; [wefaxDom.viewLiveBtn, wefaxDom.viewHistoryBtn].forEach(function(btn) { if (btn) btn.classList.remove("sat-view-active"); }); if (view === "live" && wefaxDom.viewLiveBtn) wefaxDom.viewLiveBtn.classList.add("sat-view-active"); if (view === "history" && wefaxDom.viewHistoryBtn) wefaxDom.viewHistoryBtn.classList.add("sat-view-active"); if (view === "history") renderWefaxHistoryTable(); } if (wefaxDom.viewLiveBtn) wefaxDom.viewLiveBtn.addEventListener("click", function() { switchWefaxView("live"); }); if (wefaxDom.viewHistoryBtn) wefaxDom.viewHistoryBtn.addEventListener("click", function() { switchWefaxView("history"); }); function resetLiveCanvas(pixelsPerLine) { const canvas = wefaxDom.liveCanvas; if (!canvas) return; wefaxLivePixelsPerLine = pixelsPerLine; wefaxLiveLineCount = 0; canvas.width = pixelsPerLine; canvas.height = 800; wefaxLiveCtx = canvas.getContext("2d"); if (!wefaxLiveCtx) return; wefaxLiveCtx.fillStyle = "#000"; wefaxLiveCtx.fillRect(0, 0, canvas.width, canvas.height); if (wefaxDom.liveContainer) wefaxDom.liveContainer.style.display = ""; } function paintLine(lineBytes) { const canvas = wefaxDom.liveCanvas; if (!wefaxLiveCtx || !canvas) return; const y = wefaxLiveLineCount; if (y >= canvas.height) { const old = wefaxLiveCtx.getImageData(0, 0, canvas.width, canvas.height); canvas.height *= 2; wefaxLiveCtx = canvas.getContext("2d"); if (!wefaxLiveCtx) return; wefaxLiveCtx.putImageData(old, 0, 0); } const w = wefaxLivePixelsPerLine; const imgData = wefaxLiveCtx.createImageData(w, 1); const d = imgData.data; for (let x = 0; x < w; x++) { const v = lineBytes[x] ?? 0; const i = x * 4; d[i] = v; d[i + 1] = v; d[i + 2] = v; d[i + 3] = 255; } wefaxLiveCtx.putImageData(imgData, 0, y); wefaxLiveLineCount++; } function wefaxRigImages() { return forActiveRig(wefaxImageHistory); } function renderWefaxLatestCard() { if (!wefaxDom.liveLatest) return; const rigImages = wefaxRigImages(); if (rigImages.length === 0) { wefaxDom.liveLatest.innerHTML = '
No images decoded yet. Enable the decoder and tune to a WEFAX station.
'; return; } const img = rigImages[0]; if (!img) return; const ts = img._ts || "--"; const date = img._tsMs ? new Date(img._tsMs).toLocaleDateString() : ""; const meta = [ `${String(img.ioc ?? "--")} IOC`, `${String(img.lpm ?? "--")} LPM`, `${String(img.line_count ?? 0)} lines`, `${date} ${ts}` ].join(" · "); const imgSrc = img._dataUrl ? img._dataUrl : img.path ? "/images/" + escapeHtml(img.path.split("/").pop()) : null; let html = '
'; html += '
Latest decoded image
'; html += '
' + escapeHtml(meta) + "
"; if (imgSrc) { html += 'View full image'; } html += "
"; wefaxDom.liveLatest.innerHTML = html; } function getWefaxFilteredHistory() { let items = wefaxRigImages(); if (wefaxFilterText) { items = items.filter(function(i) { const haystack = [ String(i.ioc || ""), String(i.lpm || ""), String(i.line_count || "") ].join(" ").toUpperCase(); return haystack.indexOf(wefaxFilterText) >= 0; }); } const sortVal = wefaxDom.sortSelect ? wefaxDom.sortSelect.value : "newest"; if (sortVal === "oldest") items = items.slice().reverse(); return items; } function renderWefaxHistoryRow(img) { const row = document.createElement("div"); row.className = "sat-history-row"; const ts = img._ts || "--"; const date = img._tsMs ? new Date(img._tsMs).toLocaleDateString([], { month: "short", day: "numeric" }) : ""; const ioc = img.ioc || "--"; const lpm = img.lpm || "--"; const lines = img.line_count || 0; const imgSrc = img._dataUrl ? img._dataUrl : img.path ? "/images/" + escapeHtml(img.path.split("/").pop()) : null; const link = imgSrc ? 'View' : "--"; row.innerHTML = [ "" + escapeHtml(date + " " + ts) + "", "" + escapeHtml(String(ioc)) + "", "" + escapeHtml(String(lpm)) + "", `${String(lines)}`, "" + link + "" ].join(""); return row; } function renderWefaxHistoryTable() { if (!wefaxDom.historyList) return; pruneWefaxHistory(); const items = getWefaxFilteredHistory(); const fragment = document.createDocumentFragment(); for (const item of items) { fragment.appendChild(renderWefaxHistoryRow(item)); } wefaxDom.historyList.replaceChildren(fragment); if (wefaxDom.historyCount) { const total = wefaxRigImages().length; const shown = items.length; wefaxDom.historyCount.textContent = total === 0 ? "No images yet" : shown === total ? `${String(total)} image${total === 1 ? "" : "s"}` : `${String(shown)} of ${String(total)} images`; } } function addWefaxImage(msg) { const tsMs = Number.isFinite(msg.ts_ms) ? Number(msg.ts_ms) : Date.now(); msg._tsMs = tsMs; msg._ts = new Date(tsMs).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" }); const canvas = wefaxDom.liveCanvas; if (wefaxLiveCtx && canvas && wefaxLiveLineCount > 0) { const trimmed = wefaxLiveCtx.getImageData(0, 0, canvas.width, wefaxLiveLineCount); canvas.height = wefaxLiveLineCount; wefaxLiveCtx = canvas.getContext("2d"); if (!wefaxLiveCtx) return; wefaxLiveCtx.putImageData(trimmed, 0, 0); try { msg._dataUrl = canvas.toDataURL("image/png"); } catch { } } wefaxImageHistory.unshift(msg); if (wefaxImageHistory.length > WEFAX_MAX_IMAGES) { wefaxImageHistory = wefaxImageHistory.slice(0, WEFAX_MAX_IMAGES); } scheduleWefaxUi("wefax-latest", renderWefaxLatestCard); if (wefaxActiveView === "history") { scheduleWefaxUi("wefax-history", renderWefaxHistoryTable); } } function onServerWefaxProgress(msg) { if (!isActiveRigDecode(msg.rig_id)) return; if (msg.state && !msg.line_data) { if (wefaxDom.status) { wefaxDom.status.textContent = msg.state; wefaxDom.status.style.color = msg.state.indexOf("Idle") === 0 ? "" : "var(--text-accent)"; } return; } if ((msg.line_count ?? 0) <= 1 || !wefaxLiveCtx) { resetLiveCanvas(msg.pixels_per_line || 1809); } if (msg.line_data) { const binary = atob(msg.line_data); const bytes = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i); paintLine(bytes); } if (wefaxDom.liveInfo) { wefaxDom.liveInfo.textContent = `Line ${String(msg.line_count ?? 0)} · ${String(msg.ioc ?? "--")} IOC · ${String(msg.lpm ?? "--")} LPM`; } if (wefaxDom.status) { wefaxDom.status.textContent = `Receiving — line ${String(msg.line_count ?? 0)}`; wefaxDom.status.style.color = "var(--text-accent)"; } } function onServerWefax(msg) { addWefaxImage(msg); if (!isActiveRigDecode(msg.rig_id)) return; if (wefaxDom.liveContainer) wefaxDom.liveContainer.style.display = "none"; if (wefaxDom.status) { wefaxDom.status.textContent = `Complete — ${String(msg.line_count ?? 0)} lines`; wefaxDom.status.style.color = ""; } } function restoreWefaxHistory(messages) { if (!messages.length) return; for (const message of messages) { const tsMs = Number.isFinite(message.ts_ms) ? Number(message.ts_ms) : Date.now(); message._tsMs = tsMs; message._ts = new Date(tsMs).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" }); } wefaxImageHistory = messages.concat(wefaxImageHistory); pruneWefaxHistory(); scheduleWefaxUi("wefax-latest", renderWefaxLatestCard); if (wefaxActiveView === "history") { scheduleWefaxUi("wefax-history", renderWefaxHistoryTable); } } function pruneWefaxHistoryView() { pruneWefaxHistory(); renderWefaxHistoryTable(); renderWefaxLatestCard(); } function resetWefaxHistoryView() { wefaxImageHistory = []; if (wefaxDom.historyList) wefaxDom.historyList.innerHTML = ""; if (wefaxDom.liveContainer) wefaxDom.liveContainer.style.display = "none"; wefaxLiveCtx = null; wefaxLiveLineCount = 0; renderWefaxLatestCard(); renderWefaxHistoryTable(); if (wefaxDom.status) { wefaxDom.status.textContent = "Idle"; wefaxDom.status.style.color = ""; } } if (wefaxDom.filterInput) { const filterInput = wefaxDom.filterInput; wefaxDom.filterInput.addEventListener("input", function() { wefaxFilterText = filterInput.value.trim().toUpperCase(); scheduleWefaxUi("wefax-history", renderWefaxHistoryTable); }); } if (wefaxDom.sortSelect) { wefaxDom.sortSelect.addEventListener("change", function() { scheduleWefaxUi("wefax-history", renderWefaxHistoryTable); }); } wefaxWindow.syncWefaxToggle = function(enabled) { if (!wefaxDom.toggleBtn) return; wefaxDom.toggleBtn.dataset.enabled = enabled ? "true" : "false"; wefaxDom.toggleBtn.textContent = enabled ? "Disable WEFAX" : "Enable WEFAX"; wefaxDom.toggleBtn.style.borderColor = enabled ? "#00d17f" : ""; wefaxDom.toggleBtn.style.color = enabled ? "#00d17f" : ""; }; if (wefaxDom.toggleBtn) { const toggleButton = wefaxDom.toggleBtn; wefaxDom.toggleBtn.addEventListener("click", () => { void (async () => { try { if (wefaxWindow.takeSchedulerControlForDecoderDisable) { await wefaxWindow.takeSchedulerControlForDecoderDisable(toggleButton); } await hostCore.postPath("/toggle_wefax_decode"); } catch (e) { console.error("WEFAX toggle failed", e); } })(); }); } if (wefaxDom.clearBtn) { wefaxDom.clearBtn.addEventListener("click", () => { void (async () => { try { await hostCore.postPath("/clear_wefax_decode"); resetWefaxHistoryView(); } catch (e) { console.error("WEFAX clear failed", e); } })(); }); } renderWefaxLatestCard(); wefaxWindow.trxPluginRuntime.registerDecoder({ id: "wefax", onMessage: onServerWefax, restore: restoreWefaxHistory, prune: pruneWefaxHistoryView, reset: resetWefaxHistoryView, rerender: () => { renderWefaxLatestCard(); renderWefaxHistoryTable(); } }); wefaxWindow.trxPluginRuntime.registerDecoder({ id: "wefax_progress", onMessage: onServerWefaxProgress });