diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/background-decode.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/background-decode.js
index 49400519..a425cb37 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/background-decode.js
+++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/background-decode.js
@@ -1,355 +1,365 @@
"use strict";
-(function() {
- "use strict";
- function bgdSupportedIds() {
- return (window.decoderRegistry || []).filter(function(d) {
- return d.background_decode;
- }).map(function(d) {
- return d.id;
- });
- }
- let backgroundDecodeRole = null;
- let currentRigId = null;
- let currentConfig = null;
- let bookmarkList = [];
- let statusInterval = null;
- let bgdDirty = false;
- function initBackgroundDecode(rigId, role) {
- backgroundDecodeRole = role;
- currentRigId = rigId || null;
- if (currentRigId) loadBackgroundDecode();
- startStatusPolling();
- }
- function setBackgroundDecodeRig(rigId) {
- const nextRigId = rigId || null;
- if (nextRigId === currentRigId) return;
- currentRigId = nextRigId;
- if (!currentRigId) return;
- loadBackgroundDecode();
- }
- function apiGetConfig(rigId) {
- return fetch("/background-decode/" + encodeURIComponent(rigId)).then(function(r) {
- if (!r.ok) throw new Error("HTTP " + r.status);
- return r.json();
- });
- }
- function apiPutConfig(rigId, config) {
- return fetch("/background-decode/" + encodeURIComponent(rigId), {
- method: "PUT",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify(config)
- }).then(function(r) {
- if (!r.ok) throw new Error("HTTP " + r.status);
- return r.json();
- });
- }
- function apiResetConfig(rigId) {
- return fetch("/background-decode/" + encodeURIComponent(rigId), {
- method: "DELETE"
- }).then(function(r) {
- if (!r.ok) throw new Error("HTTP " + r.status);
- return r.json();
- });
- }
- function apiGetStatus(rigId) {
- return fetch("/background-decode/" + encodeURIComponent(rigId) + "/status").then(function(r) {
- if (!r.ok) throw new Error("HTTP " + r.status);
- return r.json();
- });
- }
- function apiGetBookmarks() {
- return fetch("/bookmarks").then(function(r) {
- if (!r.ok) throw new Error("HTTP " + r.status);
- return r.json();
- });
- }
- function loadBackgroundDecode() {
- const rigId = currentRigId;
- if (!rigId) return;
- Promise.all([apiGetConfig(rigId), apiGetBookmarks()]).then(function([config, bookmarks]) {
- currentConfig = config || { remote: rigId, enabled: false, bookmark_ids: [] };
- bookmarkList = Array.isArray(bookmarks) ? bookmarks : [];
- renderBackgroundDecode();
- clearBgdDirty();
- pollBackgroundDecodeStatus();
- }).catch(function(err) {
- console.error("background decode load failed", err);
- });
- }
- function supportedBookmarks() {
- return bookmarkList.filter(function(bookmark) {
- return bookmarkDecoderKinds(bookmark).length > 0;
- });
- }
- function bookmarkDecoderKinds(bookmark) {
- var ids = bgdSupportedIds();
- var decoders = Array.isArray(bookmark && bookmark.decoders) ? bookmark.decoders : [];
- var explicit = decoders.map(function(item) {
- return String(item || "").trim().toLowerCase();
- }).filter(function(item, index, arr) {
- return ids.indexOf(item) >= 0 && arr.indexOf(item) === index;
- });
- if (explicit.length > 0) return explicit;
- var mode = String(bookmark && bookmark.mode || "").trim().toUpperCase();
- return (window.decoderRegistry || []).filter(function(d) {
- return d.activation === "mode_bound" && d.background_decode && d.active_modes.indexOf(mode) >= 0;
- }).map(function(d) {
- return d.id;
- });
- }
- function renderBackgroundDecode() {
- if (!currentConfig) {
- currentConfig = { remote: currentRigId, enabled: false, bookmark_ids: [] };
- }
- setCheckbox("background-decode-enabled", !!currentConfig.enabled);
- renderBookmarkChecklist();
- const isControl = backgroundDecodeRole === "control" || typeof authEnabled !== "undefined" && !authEnabled;
- const panel = document.getElementById("background-decode-panel");
- if (panel) {
- panel.querySelectorAll("input, select, button.sch-write").forEach(function(el) {
- el.disabled = !isControl;
+(() => {
+ // src/plugins/background-decode.ts
+ var bgdWindow = window;
+ (function() {
+ "use strict";
+ function bgdSupportedIds() {
+ return (bgdWindow.decoderRegistry || []).filter(function(d) {
+ return d.background_decode;
+ }).map(function(d) {
+ return d.id;
});
}
- const saveBtn = document.getElementById("background-decode-save-btn");
- const resetBtn = document.getElementById("background-decode-reset-btn");
- if (saveBtn) saveBtn.style.display = isControl ? "" : "none";
- if (resetBtn) resetBtn.style.display = isControl ? "" : "none";
- }
- function renderBookmarkChecklist(filterText) {
- const container = document.getElementById("bgd-bookmark-checklist");
- if (!container) return;
- container.innerHTML = "";
- const selectedIds = new Set(
- currentConfig && Array.isArray(currentConfig.bookmark_ids) ? currentConfig.bookmark_ids : []
- );
- const all = supportedBookmarks();
- const filter = (filterText || "").trim().toLowerCase();
- const filtered = filter ? all.filter(function(bm) {
- var text = (bm.name + " " + formatFreq(bm.freq_hz) + " " + bm.mode).toLowerCase();
- return text.indexOf(filter) >= 0;
- }) : all;
- if (filtered.length === 0) {
- container.innerHTML = '
' + (all.length === 0 ? "No supported bookmarks available." : "No bookmarks match filter.") + "
";
- return;
+ let backgroundDecodeRole = null;
+ let currentRigId = null;
+ let currentConfig = null;
+ let bookmarkList = [];
+ let statusInterval = null;
+ let bgdDirty = false;
+ function initBackgroundDecode(rigId, role) {
+ backgroundDecodeRole = role;
+ currentRigId = rigId || null;
+ if (currentRigId) loadBackgroundDecode();
+ startStatusPolling();
}
- filtered.forEach(function(bookmark) {
- var row = document.createElement("label");
- row.className = "bgd-checklist-row";
- var decoders = bookmarkDecoderKinds(bookmark);
- var checked = selectedIds.has(bookmark.id) ? " checked" : "";
- row.innerHTML = '' + escHtml(bookmark.name) + '' + escHtml(formatFreq(bookmark.freq_hz) + " " + bookmark.mode + " · " + decoders.join("/").toUpperCase()) + "";
- row.querySelector("input").addEventListener("change", function(e) {
- onChecklistToggle(bookmark.id, e.target.checked);
- });
- container.appendChild(row);
- });
- }
- function onChecklistToggle(bookmarkId, checked) {
- if (!currentConfig) {
- currentConfig = { remote: currentRigId, enabled: false, bookmark_ids: [] };
+ function setBackgroundDecodeRig(rigId) {
+ const nextRigId = rigId || null;
+ if (nextRigId === currentRigId) return;
+ currentRigId = nextRigId;
+ if (!currentRigId) return;
+ loadBackgroundDecode();
}
- if (!Array.isArray(currentConfig.bookmark_ids)) currentConfig.bookmark_ids = [];
- if (checked && !currentConfig.bookmark_ids.includes(bookmarkId)) {
- currentConfig.bookmark_ids.push(bookmarkId);
- } else if (!checked) {
- currentConfig.bookmark_ids = currentConfig.bookmark_ids.filter(function(id) {
- return id !== bookmarkId;
+ function apiGetConfig(rigId) {
+ return fetch("/background-decode/" + encodeURIComponent(rigId)).then(function(r) {
+ if (!r.ok) throw new Error(`HTTP ${String(r.status)}`);
+ return r.json();
});
}
- markBgdDirty();
- }
- function saveBackgroundDecode() {
- const rigId = currentRigId;
- if (!rigId) return;
- const payload = {
- remote: rigId,
- enabled: !!document.getElementById("background-decode-enabled").checked,
- bookmark_ids: Array.isArray(currentConfig && currentConfig.bookmark_ids) ? currentConfig.bookmark_ids.slice() : []
- };
- const btn = document.getElementById("background-decode-save-btn");
- if (btn) btn.disabled = true;
- apiPutConfig(rigId, payload).then(function(saved) {
- currentConfig = saved;
- renderBackgroundDecode();
- clearBgdDirty();
- pollBackgroundDecodeStatus();
- showToast("Background decode saved.");
- }).catch(function(err) {
- showToast("Save failed: " + err.message, true);
- }).finally(function() {
- if (btn) btn.disabled = false;
- });
- }
- async function resetBackgroundDecode() {
- const rigId = currentRigId;
- if (!rigId) return;
- if (!await window.trxUi.confirm({ title: "Reset background decoding?", message: "The current background decode configuration will be permanently reset.", confirmLabel: "Reset" })) return;
- apiResetConfig(rigId).then(function(saved) {
- currentConfig = saved;
- renderBackgroundDecode();
- clearBgdDirty();
- pollBackgroundDecodeStatus();
- showToast("Background decode reset.");
- }).catch(function(err) {
- showToast("Reset failed: " + err.message, true);
- });
- }
- function startStatusPolling() {
- if (statusInterval) clearInterval(statusInterval);
- statusInterval = setInterval(pollBackgroundDecodeStatus, 15e3);
- }
- function pollBackgroundDecodeStatus() {
- const rigId = currentRigId;
- if (!rigId) return;
- apiGetStatus(rigId).then(renderStatus).catch(function() {
- });
- }
- function renderStatus(status) {
- const card = document.getElementById("background-decode-status-card");
- if (!card) return;
- const entries = Array.isArray(status && status.entries) ? status.entries : [];
- if (!entries.length) {
- card.textContent = "No background decode bookmarks configured.";
- return;
+ function apiPutConfig(rigId, config) {
+ return fetch("/background-decode/" + encodeURIComponent(rigId), {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(config)
+ }).then(function(r) {
+ if (!r.ok) throw new Error(`HTTP ${String(r.status)}`);
+ return r.json();
+ });
}
- const summary = [];
- if (status.active_rig) {
- if (Number.isFinite(status.center_hz)) summary.push("Center " + formatFreq(status.center_hz));
- if (Number.isFinite(status.sample_rate) && status.sample_rate > 0) summary.push("Span ±" + formatFreq(status.sample_rate / 2));
- } else {
- summary.push("This rig is not currently selected for audio.");
+ function apiResetConfig(rigId) {
+ return fetch("/background-decode/" + encodeURIComponent(rigId), {
+ method: "DELETE"
+ }).then(function(r) {
+ if (!r.ok) throw new Error(`HTTP ${String(r.status)}`);
+ return r.json();
+ });
}
- let html = summary.length ? '' + escHtml(summary.join(" · ")) + "
" : "";
- html += '';
- entries.forEach(function(entry) {
- const name = entry.bookmark_name || entry.bookmark_id || "Unknown bookmark";
- const parts = [];
- if (Number.isFinite(entry.freq_hz)) parts.push(formatFreq(entry.freq_hz));
- if (entry.mode) parts.push(entry.mode);
- if (Array.isArray(entry.decoder_kinds) && entry.decoder_kinds.length) {
- parts.push(entry.decoder_kinds.join("/").toUpperCase());
+ function apiGetStatus(rigId) {
+ return fetch("/background-decode/" + encodeURIComponent(rigId) + "/status").then(function(r) {
+ if (!r.ok) throw new Error(`HTTP ${String(r.status)}`);
+ return r.json();
+ });
+ }
+ function apiGetBookmarks() {
+ return fetch("/bookmarks").then(function(r) {
+ if (!r.ok) throw new Error(`HTTP ${String(r.status)}`);
+ return r.json();
+ });
+ }
+ function loadBackgroundDecode() {
+ const rigId = currentRigId;
+ if (!rigId) return;
+ Promise.all([apiGetConfig(rigId), apiGetBookmarks()]).then(function([config, bookmarks]) {
+ currentConfig = config;
+ bookmarkList = Array.isArray(bookmarks) ? bookmarks : [];
+ renderBackgroundDecode();
+ clearBgdDirty();
+ pollBackgroundDecodeStatus();
+ }).catch(function(err) {
+ console.error("background decode load failed", err);
+ });
+ }
+ function supportedBookmarks() {
+ return bookmarkList.filter(function(bookmark) {
+ return bookmarkDecoderKinds(bookmark).length > 0;
+ });
+ }
+ function bookmarkDecoderKinds(bookmark) {
+ const ids = bgdSupportedIds();
+ const decoders = bookmark.decoders ?? [];
+ const explicit = decoders.map(function(item) {
+ return item.trim().toLowerCase();
+ }).filter(function(item, index, arr) {
+ return ids.indexOf(item) >= 0 && arr.indexOf(item) === index;
+ });
+ if (explicit.length > 0) return explicit;
+ const mode = bookmark.mode.trim().toUpperCase();
+ return (bgdWindow.decoderRegistry || []).filter(function(d) {
+ return d.activation === "mode_bound" && d.background_decode && d.active_modes.indexOf(mode) >= 0;
+ }).map(function(d) {
+ return d.id;
+ });
+ }
+ function renderBackgroundDecode() {
+ if (!currentConfig) {
+ currentConfig = { remote: currentRigId, enabled: false, bookmark_ids: [] };
}
- html += '
' + escHtml(name) + '
' + escHtml(parts.join(" · ")) + '
' + escHtml(prettyState(entry.state)) + "
";
- });
- html += "
";
- card.innerHTML = html;
- }
- function prettyState(state) {
- switch (state) {
- case "active":
- return "✓ Active";
- case "out_of_span":
- return "△ Out of span";
- case "waiting_for_spectrum":
- return "△ Waiting";
- case "waiting_for_user":
- return "△ No user";
- case "missing_bookmark":
- return "✗ Missing";
- case "no_supported_decoders":
- return "✗ Unsupported";
- case "disabled":
- return "△ Disabled";
- case "handled_by_scheduler":
- return "△ Scheduler";
- case "scheduler_has_control":
- return "△ Scheduler";
- case "handled_by_virtual_channel":
- return "△ VChan";
- default:
- return "△ Inactive";
+ setCheckbox("background-decode-enabled", currentConfig.enabled);
+ renderBookmarkChecklist();
+ const isControl = backgroundDecodeRole === "control" || bgdWindow.authEnabled === false;
+ const panel = document.getElementById("background-decode-panel");
+ if (panel) {
+ panel.querySelectorAll("input, select, button.sch-write").forEach(function(el) {
+ el.disabled = !isControl;
+ });
+ }
+ const saveBtn = document.getElementById("background-decode-save-btn");
+ const resetBtn = document.getElementById("background-decode-reset-btn");
+ if (saveBtn) saveBtn.style.display = isControl ? "" : "none";
+ if (resetBtn) resetBtn.style.display = isControl ? "" : "none";
}
- }
- function setCheckbox(id, value) {
- const el = document.getElementById(id);
- if (el) el.checked = !!value;
- }
- function formatFreq(hz) {
- if (!Number.isFinite(hz) || hz <= 0) return "--";
- if (hz >= 1e6) return (hz / 1e6).toFixed(3).replace(/\.?0+$/, "") + " MHz";
- if (hz >= 1e3) return (hz / 1e3).toFixed(1).replace(/\.?0+$/, "") + " kHz";
- return hz + " Hz";
- }
- function escHtml(value) {
- return String(value == null ? "" : value).replace(/&/g, "&").replace(//g, ">").replace(/"/g, """);
- }
- function markBgdDirty() {
- if (bgdDirty) return;
- bgdDirty = true;
- var btn = document.getElementById("background-decode-save-btn");
- if (btn) btn.classList.add("sch-dirty");
- }
- function clearBgdDirty() {
- bgdDirty = false;
- var btn = document.getElementById("background-decode-save-btn");
- if (btn) btn.classList.remove("sch-dirty");
- }
- function showToast(msg, isError) {
- const el = document.getElementById("background-decode-toast");
- if (!el) return;
- el.textContent = msg;
- el.style.background = isError ? "var(--color-error, #c00)" : "var(--accent-green)";
- el.style.display = "block";
- setTimeout(function() {
- el.style.display = "none";
- }, 3e3);
- }
- function selectAllBookmarks() {
- if (!currentConfig) {
- currentConfig = { remote: currentRigId, enabled: false, bookmark_ids: [] };
- }
- var ids = supportedBookmarks().map(function(bm) {
- return bm.id;
- });
- currentConfig.bookmark_ids = ids;
- renderBookmarkChecklist(document.getElementById("bgd-bookmark-filter")?.value);
- markBgdDirty();
- }
- function deselectAllBookmarks() {
- if (!currentConfig) {
- currentConfig = { remote: currentRigId, enabled: false, bookmark_ids: [] };
- }
- currentConfig.bookmark_ids = [];
- renderBookmarkChecklist(document.getElementById("bgd-bookmark-filter")?.value);
- markBgdDirty();
- }
- function wireBackgroundDecodeEvents() {
- const filterInput = document.getElementById("bgd-bookmark-filter");
- if (filterInput && !filterInput._wired) {
- filterInput._wired = true;
- filterInput.addEventListener("input", function() {
- renderBookmarkChecklist(filterInput.value);
+ function renderBookmarkChecklist(filterText = "") {
+ const container = document.getElementById("bgd-bookmark-checklist");
+ if (!container) return;
+ container.innerHTML = "";
+ const selectedIds = new Set(
+ currentConfig && Array.isArray(currentConfig.bookmark_ids) ? currentConfig.bookmark_ids : []
+ );
+ const all = supportedBookmarks();
+ const filter = (filterText || "").trim().toLowerCase();
+ const filtered = filter ? all.filter(function(bm) {
+ const text = (bm.name + " " + formatFreq(bm.freq_hz) + " " + bm.mode).toLowerCase();
+ return text.indexOf(filter) >= 0;
+ }) : all;
+ if (filtered.length === 0) {
+ container.innerHTML = '' + (all.length === 0 ? "No supported bookmarks available." : "No bookmarks match filter.") + "
";
+ return;
+ }
+ filtered.forEach(function(bookmark) {
+ const row = document.createElement("label");
+ row.className = "bgd-checklist-row";
+ const decoders = bookmarkDecoderKinds(bookmark);
+ const checked = selectedIds.has(bookmark.id) ? " checked" : "";
+ row.innerHTML = '' + escHtml(bookmark.name) + '' + escHtml(formatFreq(bookmark.freq_hz) + " " + bookmark.mode + " · " + decoders.join("/").toUpperCase()) + "";
+ row.querySelector("input")?.addEventListener("change", function(e) {
+ onChecklistToggle(bookmark.id, e.currentTarget.checked);
+ });
+ container.appendChild(row);
});
}
- const enabledCb = document.getElementById("background-decode-enabled");
- if (enabledCb && !enabledCb._wired) {
- enabledCb._wired = true;
- enabledCb.addEventListener("change", function() {
- markBgdDirty();
+ function onChecklistToggle(bookmarkId, checked) {
+ if (!currentConfig) {
+ currentConfig = { remote: currentRigId, enabled: false, bookmark_ids: [] };
+ }
+ if (!Array.isArray(currentConfig.bookmark_ids)) currentConfig.bookmark_ids = [];
+ if (checked && !currentConfig.bookmark_ids.includes(bookmarkId)) {
+ currentConfig.bookmark_ids.push(bookmarkId);
+ } else if (!checked) {
+ currentConfig.bookmark_ids = currentConfig.bookmark_ids.filter(function(id) {
+ return id !== bookmarkId;
+ });
+ }
+ markBgdDirty();
+ }
+ function saveBackgroundDecode() {
+ const rigId = currentRigId;
+ if (!rigId) return;
+ const payload = {
+ remote: rigId,
+ enabled: document.getElementById("background-decode-enabled")?.checked ?? false,
+ bookmark_ids: currentConfig?.bookmark_ids.slice() ?? []
+ };
+ const btn = document.getElementById("background-decode-save-btn");
+ if (btn) btn.disabled = true;
+ apiPutConfig(rigId, payload).then(function(saved) {
+ currentConfig = saved;
+ renderBackgroundDecode();
+ clearBgdDirty();
+ pollBackgroundDecodeStatus();
+ showToast("Background decode saved.", false);
+ }).catch(function(err) {
+ showToast(`Save failed: ${errorMessage(err)}`, true);
+ }).finally(function() {
+ if (btn) btn.disabled = false;
});
}
- const selectAllBtn = document.getElementById("bgd-select-all-btn");
- if (selectAllBtn && !selectAllBtn._wired) {
- selectAllBtn._wired = true;
- selectAllBtn.addEventListener("click", selectAllBookmarks);
+ async function resetBackgroundDecode() {
+ const rigId = currentRigId;
+ if (!rigId) return;
+ if (!await bgdWindow.trxUi.confirm({ title: "Reset background decoding?", message: "The current background decode configuration will be permanently reset.", confirmLabel: "Reset" })) return;
+ apiResetConfig(rigId).then(function(saved) {
+ currentConfig = saved;
+ renderBackgroundDecode();
+ clearBgdDirty();
+ pollBackgroundDecodeStatus();
+ showToast("Background decode reset.", false);
+ }).catch(function(err) {
+ showToast(`Reset failed: ${errorMessage(err)}`, true);
+ });
}
- const deselectAllBtn = document.getElementById("bgd-deselect-all-btn");
- if (deselectAllBtn && !deselectAllBtn._wired) {
- deselectAllBtn._wired = true;
- deselectAllBtn.addEventListener("click", deselectAllBookmarks);
+ function startStatusPolling() {
+ if (statusInterval) clearInterval(statusInterval);
+ statusInterval = setInterval(pollBackgroundDecodeStatus, 15e3);
}
- const saveBtn = document.getElementById("background-decode-save-btn");
- if (saveBtn && !saveBtn._wired) {
- saveBtn._wired = true;
- saveBtn.addEventListener("click", saveBackgroundDecode);
+ function pollBackgroundDecodeStatus() {
+ const rigId = currentRigId;
+ if (!rigId) return;
+ apiGetStatus(rigId).then(renderStatus).catch(function() {
+ });
}
- const resetBtn = document.getElementById("background-decode-reset-btn");
- if (resetBtn && !resetBtn._wired) {
- resetBtn._wired = true;
- resetBtn.addEventListener("click", resetBackgroundDecode);
+ function renderStatus(status) {
+ const card = document.getElementById("background-decode-status-card");
+ if (!card) return;
+ const entries = status.entries ?? [];
+ if (!entries.length) {
+ card.textContent = "No background decode bookmarks configured.";
+ return;
+ }
+ const summary = [];
+ if (status.active_rig) {
+ if (typeof status.center_hz === "number" && Number.isFinite(status.center_hz)) summary.push("Center " + formatFreq(status.center_hz));
+ if (typeof status.sample_rate === "number" && Number.isFinite(status.sample_rate) && status.sample_rate > 0) summary.push("Span ±" + formatFreq(status.sample_rate / 2));
+ } else {
+ summary.push("This rig is not currently selected for audio.");
+ }
+ let html = summary.length ? '' + escHtml(summary.join(" · ")) + "
" : "";
+ html += '';
+ entries.forEach(function(entry) {
+ const name = entry.bookmark_name || entry.bookmark_id || "Unknown bookmark";
+ const parts = [];
+ if (typeof entry.freq_hz === "number" && Number.isFinite(entry.freq_hz)) parts.push(formatFreq(entry.freq_hz));
+ if (entry.mode) parts.push(entry.mode);
+ if (Array.isArray(entry.decoder_kinds) && entry.decoder_kinds.length) {
+ parts.push(entry.decoder_kinds.join("/").toUpperCase());
+ }
+ html += '
' + escHtml(name) + '
' + escHtml(parts.join(" · ")) + '
' + escHtml(prettyState(entry.state)) + "
";
+ });
+ html += "
";
+ card.innerHTML = html;
}
- }
- window.initBackgroundDecode = initBackgroundDecode;
- window.wireBackgroundDecodeEvents = wireBackgroundDecodeEvents;
- window.setBackgroundDecodeRig = setBackgroundDecodeRig;
+ function prettyState(state) {
+ switch (state) {
+ case "active":
+ return "✓ Active";
+ case "out_of_span":
+ return "△ Out of span";
+ case "waiting_for_spectrum":
+ return "△ Waiting";
+ case "waiting_for_user":
+ return "△ No user";
+ case "missing_bookmark":
+ return "✗ Missing";
+ case "no_supported_decoders":
+ return "✗ Unsupported";
+ case "disabled":
+ return "△ Disabled";
+ case "handled_by_scheduler":
+ return "△ Scheduler";
+ case "scheduler_has_control":
+ return "△ Scheduler";
+ case "handled_by_virtual_channel":
+ return "△ VChan";
+ default:
+ return "△ Inactive";
+ }
+ }
+ function setCheckbox(id, value) {
+ const el = document.getElementById(id);
+ if (el) el.checked = value;
+ }
+ function formatFreq(hz) {
+ if (!Number.isFinite(hz) || hz <= 0) return "--";
+ if (hz >= 1e6) return (hz / 1e6).toFixed(3).replace(/\.?0+$/, "") + " MHz";
+ if (hz >= 1e3) return (hz / 1e3).toFixed(1).replace(/\.?0+$/, "") + " kHz";
+ return `${String(hz)} Hz`;
+ }
+ function escHtml(value) {
+ const text = typeof value === "string" || typeof value === "number" || typeof value === "boolean" ? String(value) : "";
+ return text.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """);
+ }
+ function errorMessage(error) {
+ return error instanceof Error ? error.message : String(error);
+ }
+ function markBgdDirty() {
+ if (bgdDirty) return;
+ bgdDirty = true;
+ const btn = document.getElementById("background-decode-save-btn");
+ if (btn) btn.classList.add("sch-dirty");
+ }
+ function clearBgdDirty() {
+ bgdDirty = false;
+ const btn = document.getElementById("background-decode-save-btn");
+ if (btn) btn.classList.remove("sch-dirty");
+ }
+ function showToast(msg, isError) {
+ const el = document.getElementById("background-decode-toast");
+ if (!el) return;
+ el.textContent = msg;
+ el.style.background = isError ? "var(--color-error, #c00)" : "var(--accent-green)";
+ el.style.display = "block";
+ setTimeout(function() {
+ el.style.display = "none";
+ }, 3e3);
+ }
+ function selectAllBookmarks() {
+ if (!currentConfig) {
+ currentConfig = { remote: currentRigId, enabled: false, bookmark_ids: [] };
+ }
+ const ids = supportedBookmarks().map(function(bm) {
+ return bm.id;
+ });
+ currentConfig.bookmark_ids = ids;
+ renderBookmarkChecklist(document.getElementById("bgd-bookmark-filter")?.value);
+ markBgdDirty();
+ }
+ function deselectAllBookmarks() {
+ if (!currentConfig) {
+ currentConfig = { remote: currentRigId, enabled: false, bookmark_ids: [] };
+ }
+ currentConfig.bookmark_ids = [];
+ renderBookmarkChecklist(document.getElementById("bgd-bookmark-filter")?.value);
+ markBgdDirty();
+ }
+ function wireBackgroundDecodeEvents() {
+ const filterInput = document.getElementById("bgd-bookmark-filter");
+ if (filterInput && !filterInput._wired) {
+ filterInput._wired = true;
+ filterInput.addEventListener("input", function() {
+ renderBookmarkChecklist(filterInput.value);
+ });
+ }
+ const enabledCb = document.getElementById("background-decode-enabled");
+ if (enabledCb && !enabledCb._wired) {
+ enabledCb._wired = true;
+ enabledCb.addEventListener("change", function() {
+ markBgdDirty();
+ });
+ }
+ const selectAllBtn = document.getElementById("bgd-select-all-btn");
+ if (selectAllBtn && !selectAllBtn._wired) {
+ selectAllBtn._wired = true;
+ selectAllBtn.addEventListener("click", selectAllBookmarks);
+ }
+ const deselectAllBtn = document.getElementById("bgd-deselect-all-btn");
+ if (deselectAllBtn && !deselectAllBtn._wired) {
+ deselectAllBtn._wired = true;
+ deselectAllBtn.addEventListener("click", deselectAllBookmarks);
+ }
+ const saveBtn = document.getElementById("background-decode-save-btn");
+ if (saveBtn && !saveBtn._wired) {
+ saveBtn._wired = true;
+ saveBtn.addEventListener("click", saveBackgroundDecode);
+ }
+ const resetBtn = document.getElementById("background-decode-reset-btn");
+ if (resetBtn && !resetBtn._wired) {
+ resetBtn._wired = true;
+ resetBtn.addEventListener("click", () => {
+ void resetBackgroundDecode();
+ });
+ }
+ }
+ bgdWindow.initBackgroundDecode = initBackgroundDecode;
+ bgdWindow.wireBackgroundDecodeEvents = wireBackgroundDecodeEvents;
+ bgdWindow.setBackgroundDecodeRig = setBackgroundDecodeRig;
+ })();
})();
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/plugin-loader.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/plugin-loader.js
index 026d8e7c..36edf444 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/plugin-loader.js
+++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/plugin-loader.js
@@ -10,7 +10,7 @@ const pluginGroups = {
};
const loaded = /* @__PURE__ */ new Set();
const loading = /* @__PURE__ */ new Map();
-const modulePlugins = /* @__PURE__ */ new Set(["/ft8.js", "/ft4.js", "/ft2.js", "/wspr.js", "/cw.js", "/vdes.js", "/wefax.js"]);
+const modulePlugins = /* @__PURE__ */ new Set(["/ft8.js", "/ft4.js", "/ft2.js", "/wspr.js", "/cw.js", "/vdes.js", "/wefax.js", "/background-decode.js"]);
function loadLegacyScript(path) {
return new Promise((resolve, reject) => {
const script = document.createElement("script");
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/build.mjs b/src/trx-client/trx-frontend/trx-frontend-http/frontend/build.mjs
index b1663bcc..b1753331 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/build.mjs
+++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/build.mjs
@@ -24,7 +24,6 @@ await build({
"webgl-renderer": path.join(sourceDir, "webgl-renderer.ts"),
ais: path.join(sourceDir, "plugins", "ais.js"),
aprs: path.join(sourceDir, "plugins", "aprs.js"),
- "background-decode": path.join(sourceDir, "plugins", "background-decode.js"),
bookmarks: path.join(sourceDir, "plugins", "bookmarks.js"),
"hf-aprs": path.join(sourceDir, "plugins", "hf-aprs.js"),
sat: path.join(sourceDir, "plugins", "sat.js"),
@@ -51,6 +50,7 @@ await build({
"leaflet-ais-tracksymbol": path.join(sourceDir, "leaflet-ais-tracksymbol.ts"),
vdes: path.join(sourceDir, "plugins", "vdes.ts"),
wefax: path.join(sourceDir, "plugins", "wefax.ts"),
+ "background-decode": path.join(sourceDir, "plugins", "background-decode.ts"),
},
outdir: outputDir,
bundle: true,
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugin-loader.ts b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugin-loader.ts
index fbb32d89..7edc91ce 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugin-loader.ts
+++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugin-loader.ts
@@ -16,7 +16,7 @@ const pluginGroups: Readonly> = {
const loaded = new Set();
const loading = new Map>();
-const modulePlugins = new Set(["/ft8.js", "/ft4.js", "/ft2.js", "/wspr.js", "/cw.js", "/vdes.js", "/wefax.js"]);
+const modulePlugins = new Set(["/ft8.js", "/ft4.js", "/ft2.js", "/wspr.js", "/cw.js", "/vdes.js", "/wefax.js", "/background-decode.js"]);
function loadLegacyScript(path: string): Promise {
return new Promise((resolve, reject) => {
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/background-decode.js b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/background-decode.ts
similarity index 60%
rename from src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/background-decode.js
rename to src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/background-decode.ts
index 5499f2ea..79a374eb 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/background-decode.js
+++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/src/plugins/background-decode.ts
@@ -2,30 +2,75 @@
//
// SPDX-License-Identifier: GPL-2.0-or-later
+export {};
+
+interface DecoderDescriptor {
+ id: string;
+ background_decode?: boolean;
+ activation?: string;
+ active_modes: string[];
+}
+interface Bookmark {
+ id: string;
+ name: string;
+ freq_hz: number;
+ mode: string;
+ decoders?: string[];
+}
+interface BackgroundDecodeConfig {
+ remote: string | null;
+ enabled: boolean;
+ bookmark_ids: string[];
+}
+interface BackgroundStatusEntry {
+ bookmark_name?: string;
+ bookmark_id?: string;
+ freq_hz?: number;
+ mode?: string;
+ decoder_kinds?: string[];
+ state?: string;
+}
+interface BackgroundDecodeStatus {
+ entries?: BackgroundStatusEntry[];
+ active_rig?: boolean;
+ center_hz?: number;
+ sample_rate?: number;
+}
+interface BackgroundBridge {
+ decoderRegistry?: DecoderDescriptor[];
+ authEnabled?: boolean;
+ trxUi: { confirm(options: { title: string; message: string; confirmLabel: string }): Promise };
+ initBackgroundDecode?: (rigId: string | null, role: string | null) => void;
+ wireBackgroundDecodeEvents?: () => void;
+ setBackgroundDecodeRig?: (rigId: string | null) => void;
+}
+interface WiredElement extends HTMLElement { _wired?: boolean }
+const bgdWindow = window as unknown as BackgroundBridge;
+
(function () {
"use strict";
- function bgdSupportedIds() {
- return (window.decoderRegistry || [])
+ function bgdSupportedIds(): string[] {
+ return (bgdWindow.decoderRegistry || [])
.filter(function (d) { return d.background_decode; })
.map(function (d) { return d.id; });
}
- let backgroundDecodeRole = null;
- let currentRigId = null;
- let currentConfig = null;
- let bookmarkList = [];
- let statusInterval = null;
+ let backgroundDecodeRole: string | null = null;
+ let currentRigId: string | null = null;
+ let currentConfig: BackgroundDecodeConfig | null = null;
+ let bookmarkList: Bookmark[] = [];
+ let statusInterval: ReturnType | null = null;
let bgdDirty = false;
- function initBackgroundDecode(rigId, role) {
+ function initBackgroundDecode(rigId: string | null, role: string | null): void {
backgroundDecodeRole = role;
currentRigId = rigId || null;
if (currentRigId) loadBackgroundDecode();
startStatusPolling();
}
- function setBackgroundDecodeRig(rigId) {
+ function setBackgroundDecodeRig(rigId: string | null): void {
const nextRigId = rigId || null;
if (nextRigId === currentRigId) return;
currentRigId = nextRigId;
@@ -33,44 +78,44 @@
loadBackgroundDecode();
}
- function apiGetConfig(rigId) {
+ function apiGetConfig(rigId: string): Promise {
return fetch("/background-decode/" + encodeURIComponent(rigId)).then(function (r) {
- if (!r.ok) throw new Error("HTTP " + r.status);
- return r.json();
+ if (!r.ok) throw new Error(`HTTP ${String(r.status)}`);
+ return r.json() as Promise;
});
}
- function apiPutConfig(rigId, config) {
+ function apiPutConfig(rigId: string, config: BackgroundDecodeConfig): Promise {
return fetch("/background-decode/" + encodeURIComponent(rigId), {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(config),
}).then(function (r) {
- if (!r.ok) throw new Error("HTTP " + r.status);
- return r.json();
+ if (!r.ok) throw new Error(`HTTP ${String(r.status)}`);
+ return r.json() as Promise;
});
}
- function apiResetConfig(rigId) {
+ function apiResetConfig(rigId: string): Promise {
return fetch("/background-decode/" + encodeURIComponent(rigId), {
method: "DELETE",
}).then(function (r) {
- if (!r.ok) throw new Error("HTTP " + r.status);
- return r.json();
+ if (!r.ok) throw new Error(`HTTP ${String(r.status)}`);
+ return r.json() as Promise;
});
}
- function apiGetStatus(rigId) {
+ function apiGetStatus(rigId: string): Promise {
return fetch("/background-decode/" + encodeURIComponent(rigId) + "/status").then(function (r) {
- if (!r.ok) throw new Error("HTTP " + r.status);
- return r.json();
+ if (!r.ok) throw new Error(`HTTP ${String(r.status)}`);
+ return r.json() as Promise;
});
}
- function apiGetBookmarks() {
+ function apiGetBookmarks(): Promise {
return fetch("/bookmarks").then(function (r) {
- if (!r.ok) throw new Error("HTTP " + r.status);
- return r.json();
+ if (!r.ok) throw new Error(`HTTP ${String(r.status)}`);
+ return r.json() as Promise;
});
}
@@ -79,35 +124,35 @@
if (!rigId) return;
Promise.all([apiGetConfig(rigId), apiGetBookmarks()])
.then(function ([config, bookmarks]) {
- currentConfig = config || { remote: rigId, enabled: false, bookmark_ids: [] };
+ currentConfig = config;
bookmarkList = Array.isArray(bookmarks) ? bookmarks : [];
renderBackgroundDecode();
clearBgdDirty();
pollBackgroundDecodeStatus();
})
- .catch(function (err) {
+ .catch(function (err: unknown) {
console.error("background decode load failed", err);
});
}
- function supportedBookmarks() {
+ function supportedBookmarks(): Bookmark[] {
return bookmarkList.filter(function (bookmark) {
return bookmarkDecoderKinds(bookmark).length > 0;
});
}
- function bookmarkDecoderKinds(bookmark) {
- var ids = bgdSupportedIds();
- var decoders = Array.isArray(bookmark && bookmark.decoders) ? bookmark.decoders : [];
- var explicit = decoders
- .map(function (item) { return String(item || "").trim().toLowerCase(); })
+ function bookmarkDecoderKinds(bookmark: Bookmark): string[] {
+ const ids = bgdSupportedIds();
+ const decoders = bookmark.decoders ?? [];
+ const explicit = decoders
+ .map(function (item) { return item.trim().toLowerCase(); })
.filter(function (item, index, arr) {
return ids.indexOf(item) >= 0 && arr.indexOf(item) === index;
});
if (explicit.length > 0) return explicit;
// Fall back: infer from mode via mode-bound entries in the registry.
- var mode = String(bookmark && bookmark.mode || "").trim().toUpperCase();
- return (window.decoderRegistry || [])
+ const mode = bookmark.mode.trim().toUpperCase();
+ return (bgdWindow.decoderRegistry || [])
.filter(function (d) {
return d.activation === "mode_bound" && d.background_decode
&& d.active_modes.indexOf(mode) >= 0;
@@ -119,13 +164,13 @@
if (!currentConfig) {
currentConfig = { remote: currentRigId, enabled: false, bookmark_ids: [] };
}
- setCheckbox("background-decode-enabled", !!currentConfig.enabled);
+ setCheckbox("background-decode-enabled", currentConfig.enabled);
renderBookmarkChecklist();
- const isControl = backgroundDecodeRole === "control" || (typeof authEnabled !== "undefined" && !authEnabled);
+ const isControl = backgroundDecodeRole === "control" || bgdWindow.authEnabled === false;
const panel = document.getElementById("background-decode-panel");
if (panel) {
- panel.querySelectorAll("input, select, button.sch-write").forEach(function (el) {
+ panel.querySelectorAll("input, select, button.sch-write").forEach(function (el) {
el.disabled = !isControl;
});
}
@@ -135,7 +180,7 @@
if (resetBtn) resetBtn.style.display = isControl ? "" : "none";
}
- function renderBookmarkChecklist(filterText) {
+ function renderBookmarkChecklist(filterText = ""): void {
const container = document.getElementById("bgd-bookmark-checklist");
if (!container) return;
container.innerHTML = "";
@@ -148,7 +193,7 @@
const filtered = filter
? all.filter(function (bm) {
- var text = (bm.name + " " + formatFreq(bm.freq_hz) + " " + bm.mode).toLowerCase();
+ const text = (bm.name + " " + formatFreq(bm.freq_hz) + " " + bm.mode).toLowerCase();
return text.indexOf(filter) >= 0;
})
: all;
@@ -161,22 +206,22 @@
}
filtered.forEach(function (bookmark) {
- var row = document.createElement("label");
+ const row = document.createElement("label");
row.className = "bgd-checklist-row";
- var decoders = bookmarkDecoderKinds(bookmark);
- var checked = selectedIds.has(bookmark.id) ? " checked" : "";
+ const decoders = bookmarkDecoderKinds(bookmark);
+ const checked = selectedIds.has(bookmark.id) ? " checked" : "";
row.innerHTML =
'' +
'' + escHtml(bookmark.name) + '' +
'' + escHtml(formatFreq(bookmark.freq_hz) + " " + bookmark.mode + " · " + decoders.join("/").toUpperCase()) + '';
- row.querySelector("input").addEventListener("change", function (e) {
- onChecklistToggle(bookmark.id, e.target.checked);
+ row.querySelector("input")?.addEventListener("change", function (e) {
+ onChecklistToggle(bookmark.id, (e.currentTarget as HTMLInputElement).checked);
});
container.appendChild(row);
});
}
- function onChecklistToggle(bookmarkId, checked) {
+ function onChecklistToggle(bookmarkId: string, checked: boolean): void {
if (!currentConfig) {
currentConfig = { remote: currentRigId, enabled: false, bookmark_ids: [] };
}
@@ -192,12 +237,12 @@
function saveBackgroundDecode() {
const rigId = currentRigId;
if (!rigId) return;
- const payload = {
+ const payload: BackgroundDecodeConfig = {
remote: rigId,
- enabled: !!document.getElementById("background-decode-enabled").checked,
- bookmark_ids: Array.isArray(currentConfig && currentConfig.bookmark_ids) ? currentConfig.bookmark_ids.slice() : [],
+ enabled: (document.getElementById("background-decode-enabled") as HTMLInputElement | null)?.checked ?? false,
+ bookmark_ids: currentConfig?.bookmark_ids.slice() ?? [],
};
- const btn = document.getElementById("background-decode-save-btn");
+ const btn = document.getElementById("background-decode-save-btn") as HTMLButtonElement | null;
if (btn) btn.disabled = true;
apiPutConfig(rigId, payload)
.then(function (saved) {
@@ -205,10 +250,10 @@
renderBackgroundDecode();
clearBgdDirty();
pollBackgroundDecodeStatus();
- showToast("Background decode saved.");
+ showToast("Background decode saved.", false);
})
- .catch(function (err) {
- showToast("Save failed: " + err.message, true);
+ .catch(function (err: unknown) {
+ showToast(`Save failed: ${errorMessage(err)}`, true);
})
.finally(function () {
if (btn) btn.disabled = false;
@@ -218,17 +263,17 @@
async function resetBackgroundDecode() {
const rigId = currentRigId;
if (!rigId) return;
- if (!await window.trxUi.confirm({ title: "Reset background decoding?", message: "The current background decode configuration will be permanently reset.", confirmLabel: "Reset" })) return;
+ if (!await bgdWindow.trxUi.confirm({ title: "Reset background decoding?", message: "The current background decode configuration will be permanently reset.", confirmLabel: "Reset" })) return;
apiResetConfig(rigId)
.then(function (saved) {
currentConfig = saved;
renderBackgroundDecode();
clearBgdDirty();
pollBackgroundDecodeStatus();
- showToast("Background decode reset.");
+ showToast("Background decode reset.", false);
})
- .catch(function (err) {
- showToast("Reset failed: " + err.message, true);
+ .catch(function (err: unknown) {
+ showToast(`Reset failed: ${errorMessage(err)}`, true);
});
}
@@ -245,18 +290,18 @@
.catch(function () {});
}
- function renderStatus(status) {
+ function renderStatus(status: BackgroundDecodeStatus): void {
const card = document.getElementById("background-decode-status-card");
if (!card) return;
- const entries = Array.isArray(status && status.entries) ? status.entries : [];
+ const entries = status.entries ?? [];
if (!entries.length) {
card.textContent = "No background decode bookmarks configured.";
return;
}
const summary = [];
if (status.active_rig) {
- if (Number.isFinite(status.center_hz)) summary.push("Center " + formatFreq(status.center_hz));
- if (Number.isFinite(status.sample_rate) && status.sample_rate > 0) summary.push("Span ±" + formatFreq(status.sample_rate / 2));
+ if (typeof status.center_hz === "number" && Number.isFinite(status.center_hz)) summary.push("Center " + formatFreq(status.center_hz));
+ if (typeof status.sample_rate === "number" && Number.isFinite(status.sample_rate) && status.sample_rate > 0) summary.push("Span ±" + formatFreq(status.sample_rate / 2));
} else {
summary.push("This rig is not currently selected for audio.");
}
@@ -265,7 +310,7 @@
entries.forEach(function (entry) {
const name = entry.bookmark_name || entry.bookmark_id || "Unknown bookmark";
const parts = [];
- if (Number.isFinite(entry.freq_hz)) parts.push(formatFreq(entry.freq_hz));
+ if (typeof entry.freq_hz === "number" && Number.isFinite(entry.freq_hz)) parts.push(formatFreq(entry.freq_hz));
if (entry.mode) parts.push(entry.mode);
if (Array.isArray(entry.decoder_kinds) && entry.decoder_kinds.length) {
parts.push(entry.decoder_kinds.join("/").toUpperCase());
@@ -285,7 +330,7 @@
card.innerHTML = html;
}
- function prettyState(state) {
+ function prettyState(state: string | undefined): string {
switch (state) {
case "active": return "\u2713 Active";
case "out_of_span": return "\u25B3 Out of span";
@@ -301,40 +346,47 @@
}
}
- function setCheckbox(id, value) {
- const el = document.getElementById(id);
- if (el) el.checked = !!value;
+ function setCheckbox(id: string, value: boolean): void {
+ const el = document.getElementById(id) as HTMLInputElement | null;
+ if (el) el.checked = value;
}
- function formatFreq(hz) {
+ function formatFreq(hz: number): string {
if (!Number.isFinite(hz) || hz <= 0) return "--";
if (hz >= 1e6) return (hz / 1e6).toFixed(3).replace(/\.?0+$/, "") + " MHz";
if (hz >= 1e3) return (hz / 1e3).toFixed(1).replace(/\.?0+$/, "") + " kHz";
- return hz + " Hz";
+ return `${String(hz)} Hz`;
}
- function escHtml(value) {
- return String(value == null ? "" : value)
+ function escHtml(value: unknown): string {
+ const text = typeof value === "string" || typeof value === "number" || typeof value === "boolean"
+ ? String(value)
+ : "";
+ return text
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """);
}
+ function errorMessage(error: unknown): string {
+ return error instanceof Error ? error.message : String(error);
+ }
+
function markBgdDirty() {
if (bgdDirty) return;
bgdDirty = true;
- var btn = document.getElementById("background-decode-save-btn");
+ const btn = document.getElementById("background-decode-save-btn");
if (btn) btn.classList.add("sch-dirty");
}
function clearBgdDirty() {
bgdDirty = false;
- var btn = document.getElementById("background-decode-save-btn");
+ const btn = document.getElementById("background-decode-save-btn");
if (btn) btn.classList.remove("sch-dirty");
}
- function showToast(msg, isError) {
+ function showToast(msg: string, isError: boolean): void {
const el = document.getElementById("background-decode-toast");
if (!el) return;
el.textContent = msg;
@@ -349,9 +401,9 @@
if (!currentConfig) {
currentConfig = { remote: currentRigId, enabled: false, bookmark_ids: [] };
}
- var ids = supportedBookmarks().map(function (bm) { return bm.id; });
+ const ids = supportedBookmarks().map(function (bm) { return bm.id; });
currentConfig.bookmark_ids = ids;
- renderBookmarkChecklist(document.getElementById("bgd-bookmark-filter")?.value);
+ renderBookmarkChecklist((document.getElementById("bgd-bookmark-filter") as HTMLInputElement | null)?.value);
markBgdDirty();
}
@@ -360,12 +412,12 @@
currentConfig = { remote: currentRigId, enabled: false, bookmark_ids: [] };
}
currentConfig.bookmark_ids = [];
- renderBookmarkChecklist(document.getElementById("bgd-bookmark-filter")?.value);
+ renderBookmarkChecklist((document.getElementById("bgd-bookmark-filter") as HTMLInputElement | null)?.value);
markBgdDirty();
}
function wireBackgroundDecodeEvents() {
- const filterInput = document.getElementById("bgd-bookmark-filter");
+ const filterInput = document.getElementById("bgd-bookmark-filter") as (HTMLInputElement & WiredElement) | null;
if (filterInput && !filterInput._wired) {
filterInput._wired = true;
filterInput.addEventListener("input", function () {
@@ -373,38 +425,38 @@
});
}
- const enabledCb = document.getElementById("background-decode-enabled");
+ const enabledCb = document.getElementById("background-decode-enabled") as (HTMLInputElement & WiredElement) | null;
if (enabledCb && !enabledCb._wired) {
enabledCb._wired = true;
enabledCb.addEventListener("change", function () { markBgdDirty(); });
}
- const selectAllBtn = document.getElementById("bgd-select-all-btn");
+ const selectAllBtn = document.getElementById("bgd-select-all-btn") as WiredElement | null;
if (selectAllBtn && !selectAllBtn._wired) {
selectAllBtn._wired = true;
selectAllBtn.addEventListener("click", selectAllBookmarks);
}
- const deselectAllBtn = document.getElementById("bgd-deselect-all-btn");
+ const deselectAllBtn = document.getElementById("bgd-deselect-all-btn") as WiredElement | null;
if (deselectAllBtn && !deselectAllBtn._wired) {
deselectAllBtn._wired = true;
deselectAllBtn.addEventListener("click", deselectAllBookmarks);
}
- const saveBtn = document.getElementById("background-decode-save-btn");
+ const saveBtn = document.getElementById("background-decode-save-btn") as WiredElement | null;
if (saveBtn && !saveBtn._wired) {
saveBtn._wired = true;
saveBtn.addEventListener("click", saveBackgroundDecode);
}
- const resetBtn = document.getElementById("background-decode-reset-btn");
+ const resetBtn = document.getElementById("background-decode-reset-btn") as WiredElement | null;
if (resetBtn && !resetBtn._wired) {
resetBtn._wired = true;
- resetBtn.addEventListener("click", resetBackgroundDecode);
+ resetBtn.addEventListener("click", () => { void resetBackgroundDecode(); });
}
}
- window.initBackgroundDecode = initBackgroundDecode;
- window.wireBackgroundDecodeEvents = wireBackgroundDecodeEvents;
- window.setBackgroundDecodeRig = setBackgroundDecodeRig;
+ bgdWindow.initBackgroundDecode = initBackgroundDecode;
+ bgdWindow.wireBackgroundDecodeEvents = wireBackgroundDecodeEvents;
+ bgdWindow.setBackgroundDecodeRig = setBackgroundDecodeRig;
})();
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/background-decode.test.mjs b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/background-decode.test.mjs
new file mode 100644
index 00000000..2b69346b
--- /dev/null
+++ b/src/trx-client/trx-frontend/trx-frontend-http/frontend/tests/background-decode.test.mjs
@@ -0,0 +1,46 @@
+// SPDX-FileCopyrightText: 2026 Stan Grams
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+import assert from "node:assert/strict";
+import { readFile } from "node:fs/promises";
+import test from "node:test";
+import vm from "node:vm";
+
+test("background decode loads configuration for the explicitly selected rig", async () => {
+ const requested = [];
+ const window = {
+ decoderRegistry: [],
+ trxUi: { confirm: async () => true },
+ };
+ const context = vm.createContext({
+ window,
+ document: { getElementById: () => null },
+ fetch: async (url) => {
+ requested.push(url);
+ return {
+ ok: true,
+ json: async () => url === "/bookmarks"
+ ? []
+ : url.endsWith("/status")
+ ? { entries: [] }
+ : { remote: "rig/a", enabled: false, bookmark_ids: [] },
+ };
+ },
+ setInterval: () => 1,
+ clearInterval() {},
+ setTimeout,
+ URL,
+ Set,
+ Error,
+ console,
+ });
+ const source = await readFile(new URL("../../assets/web/generated/background-decode.js", import.meta.url), "utf8");
+ new vm.Script(source).runInContext(context);
+
+ window.initBackgroundDecode("rig/a", "control");
+ await new Promise((resolve) => setTimeout(resolve, 0));
+ assert.ok(requested.includes("/background-decode/rig%2Fa"));
+ assert.ok(requested.includes("/bookmarks"));
+ assert.ok(requested.includes("/background-decode/rig%2Fa/status"));
+});