import { hostCore } from "./chunk-KL66PICH.js"; // src/plugins/sstv.ts var sstvWindow = window; var sstvDom = { status: document.getElementById("sstv-status"), liveView: document.getElementById("sstv-live-view"), historyView: document.getElementById("sstv-history-view"), liveContainer: document.getElementById("sstv-live-container"), liveInfo: document.getElementById("sstv-live-info"), liveCanvas: document.getElementById("sstv-live-canvas"), liveLatest: document.getElementById("sstv-live-latest"), historyList: document.getElementById("sstv-history-list"), historyCount: document.getElementById("sstv-history-count"), filterInput: document.getElementById("sstv-filter"), sortSelect: document.getElementById("sstv-sort"), toggleBtn: document.getElementById("sstv-decode-toggle-btn"), clearBtn: document.getElementById("sstv-clear-btn"), viewLiveBtn: document.getElementById("sstv-view-live"), viewHistoryBtn: document.getElementById("sstv-view-history") }; var SSTV_MAX_IMAGES = 100; var sstvHistory = []; var liveCtx = null; var liveMode = ""; var liveHeight = 0; var liveRows = 0; var activeView = "live"; var filterText = ""; function retentionMs() { return sstvWindow.getDecodeHistoryRetentionMs?.() ?? 24 * 60 * 60 * 1e3; } function pruneHistory() { const cutoff = Date.now() - retentionMs(); sstvHistory = sstvHistory.filter((image) => (image._tsMs || 0) > cutoff); } function escapeHtml(value) { return String(value).replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); } function scheduleUi(key, job) { if (typeof sstvWindow.trxScheduleUiFrameJob === "function") { sstvWindow.trxScheduleUiFrameJob(key, job); return; } job(); } function imageUrl(image) { if (!image.path) return null; const filename = image.path.split(/[\\/]/).pop(); return filename ? `/sstv-images/${encodeURIComponent(filename)}` : null; } function decodeBase64(data) { const binary = atob(data); const bytes = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i); return bytes; } function switchView(view) { activeView = view; if (sstvDom.liveView) sstvDom.liveView.style.display = view === "live" ? "" : "none"; if (sstvDom.historyView) sstvDom.historyView.style.display = view === "history" ? "" : "none"; for (const button of [sstvDom.viewLiveBtn, sstvDom.viewHistoryBtn]) { button?.classList.remove("sat-view-active"); } if (view === "live") sstvDom.viewLiveBtn?.classList.add("sat-view-active"); else sstvDom.viewHistoryBtn?.classList.add("sat-view-active"); if (view === "history") renderHistoryTable(); } sstvDom.viewLiveBtn?.addEventListener("click", () => { switchView("live"); }); sstvDom.viewHistoryBtn?.addEventListener("click", () => { switchView("history"); }); function beginPicture(mode, width, height) { const canvas = sstvDom.liveCanvas; if (!canvas || width <= 0 || height <= 0) return; liveMode = mode; liveHeight = height; liveRows = 0; canvas.width = width; canvas.height = height; liveCtx = canvas.getContext("2d"); if (!liveCtx) return; liveCtx.fillStyle = "#606060"; liveCtx.fillRect(0, 0, width, height); if (sstvDom.liveContainer) sstvDom.liveContainer.style.display = ""; updateLiveInfo(); } function updateLiveInfo() { if (!sstvDom.liveInfo) return; const canvas = sstvDom.liveCanvas; const size = canvas ? `${canvas.width}×${canvas.height}` : ""; sstvDom.liveInfo.textContent = liveMode ? `${liveMode} · ${size} · line ${liveRows}${liveHeight ? ` of ${liveHeight}` : ""}` : ""; } function paintRow(line, rgb) { const canvas = sstvDom.liveCanvas; if (!liveCtx || !canvas) return; const width = canvas.width; if (line < 0 || line >= canvas.height || rgb.length < width * 3) return; const row = liveCtx.createImageData(width, 1); for (let x = 0; x < width; x += 1) { row.data[x * 4] = rgb[x * 3] ?? 0; row.data[x * 4 + 1] = rgb[x * 3 + 1] ?? 0; row.data[x * 4 + 2] = rgb[x * 3 + 2] ?? 0; row.data[x * 4 + 3] = 255; } liveCtx.putImageData(row, 0, line); liveRows = Math.max(liveRows, line + 1); } function onProgress(msg) { if (msg.state) { if (sstvDom.status) sstvDom.status.textContent = msg.state; beginPicture(msg.mode || "", msg.width || 0, msg.height || 0); return; } if (typeof msg.line !== "number" || !msg.line_data) return; const line = msg.line; const rgb = decodeBase64(msg.line_data); scheduleUi(`sstv-row-${line}`, () => { paintRow(line, rgb); updateLiveInfo(); }); } function onImage(msg) { const image = { ...msg }; image._tsMs = typeof msg.ts_ms === "number" ? msg.ts_ms : Date.now(); image._ts = new Date(image._tsMs).toLocaleTimeString(); sstvHistory.push(image); if (sstvHistory.length > SSTV_MAX_IMAGES) sstvHistory.shift(); pruneHistory(); if (sstvDom.status) { sstvDom.status.textContent = image.complete ? `Received ${image.mode ?? "picture"}` : `Partial ${image.mode ?? "picture"} — ${image.lines ?? 0} lines`; } scheduleUi("sstv-latest", renderLatestCard); if (activeView === "history") scheduleUi("sstv-history", renderHistoryTable); } function renderLatestCard() { if (!sstvDom.liveLatest) return; const latest = sstvHistory[sstvHistory.length - 1]; if (!latest) { sstvDom.liveLatest.innerHTML = ""; return; } const url = imageUrl(latest); const lines = `${latest.lines ?? 0}${latest.height ? ` of ${latest.height}` : ""} lines`; const state = latest.complete ? "complete" : "partial"; sstvDom.liveLatest.innerHTML = `