From a91a1868d89abad0a4eacce0d7fd05aad7f21c12 Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Thu, 12 Mar 2026 22:48:24 +0100 Subject: [PATCH] [fix](trx-frontend-http): refresh background decode settings UI Co-authored-by: OpenAI Codex Signed-off-by: Stan Grams --- .../trx-frontend-http/assets/web/app.js | 1 + .../trx-frontend-http/assets/web/index.html | 2 +- .../assets/web/plugins/background-decode.js | 36 +++++++++++---- .../trx-frontend-http/assets/web/style.css | 44 +++++++----------- .../src/background_decode.rs | 45 +++++++++++++++++++ .../trx-frontend-http/src/server.rs | 1 + 6 files changed, 92 insertions(+), 37 deletions(-) diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js index 4487538..4d3708d 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js @@ -750,6 +750,7 @@ function applyRigList(activeRigId, rigIds, displayNames) { populateRigPicker(headerRigSwitchSelect, lastRigIds, activeRigId, disableSwitch); updateRigSubtitle(activeRigId); if (typeof reloadSchedulerRigSelect === "function") reloadSchedulerRigSelect(); + if (typeof reloadBackgroundDecodeRigSelect === "function") reloadBackgroundDecodeRigSelect(); } async function refreshRigList() { diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html index df33473..c0b541d 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html @@ -804,7 +804,7 @@ -
+
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/background-decode.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/background-decode.js index 35dc331..752933c 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/background-decode.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/background-decode.js @@ -26,6 +26,7 @@ if (!sel) return; const rigs = typeof getAvailableRigIds === "function" ? getAvailableRigIds() : []; if (!rigs.length) return; + const prevRigId = currentRigId; sel.innerHTML = ""; rigs.forEach(function (rigId) { const opt = document.createElement("option"); @@ -37,6 +38,11 @@ if (!currentRigId || !rigs.includes(currentRigId)) { currentRigId = rigs[0]; sel.value = currentRigId; + } else { + sel.value = currentRigId; + } + if (currentRigId && currentRigId !== prevRigId) { + loadBackgroundDecode(); } } @@ -158,14 +164,13 @@ ids.forEach(function (id) { const bookmark = bookmarkList.find(function (item) { return item.id === id; }); const chip = document.createElement("div"); - chip.className = "bgd-bookmark-chip"; + chip.className = "sch-extra-bm-tag bgd-bookmark-tag"; const decoders = bookmarkDecoderKinds(bookmark); chip.innerHTML = '' + escHtml(bookmark ? bookmark.name : id) + '' + - '' + escHtml(bookmark ? (formatFreq(bookmark.freq_hz) + " " + bookmark.mode + " · " + decoders.join("/").toUpperCase()) : "Missing bookmark") + ''; - const btn = document.createElement("button"); - btn.type = "button"; - btn.className = "bgd-bookmark-chip-remove sch-write"; + '' + escHtml(bookmark ? (formatFreq(bookmark.freq_hz) + " " + bookmark.mode + " · " + decoders.join("/").toUpperCase()) : "Missing bookmark") + ''; + const btn = document.createElement("span"); + btn.className = "sch-extra-bm-rm"; btn.textContent = "×"; btn.addEventListener("click", function () { removeBookmark(id); @@ -293,9 +298,11 @@ 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"; default: return "Inactive"; } } @@ -333,7 +340,8 @@ function wireBackgroundDecodeEvents() { const rigSel = document.getElementById("background-decode-rig-select"); - if (rigSel) { + if (rigSel && !rigSel._wired) { + rigSel._wired = true; rigSel.addEventListener("change", function () { currentRigId = rigSel.value; loadBackgroundDecode(); @@ -341,15 +349,25 @@ } const addBtn = document.getElementById("background-decode-bookmark-add"); - if (addBtn) addBtn.addEventListener("click", addBookmark); + if (addBtn && !addBtn._wired) { + addBtn._wired = true; + addBtn.addEventListener("click", addBookmark); + } const saveBtn = document.getElementById("background-decode-save-btn"); - if (saveBtn) saveBtn.addEventListener("click", saveBackgroundDecode); + if (saveBtn && !saveBtn._wired) { + saveBtn._wired = true; + saveBtn.addEventListener("click", saveBackgroundDecode); + } const resetBtn = document.getElementById("background-decode-reset-btn"); - if (resetBtn) resetBtn.addEventListener("click", resetBackgroundDecode); + if (resetBtn && !resetBtn._wired) { + resetBtn._wired = true; + resetBtn.addEventListener("click", resetBackgroundDecode); + } } window.initBackgroundDecode = initBackgroundDecode; window.wireBackgroundDecodeEvents = wireBackgroundDecodeEvents; + window.reloadBackgroundDecodeRigSelect = renderRigSelect; })(); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css index 84f6d95..3a952c5 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css @@ -3493,6 +3493,7 @@ button:focus-visible, input:focus-visible, select:focus-visible { .sch-extra-bm-rm:hover { opacity: 1; } .bgd-toggle-wrap { min-width: 18rem; + flex: 1 1 20rem; } .bgd-toggle-row { display: inline-flex; @@ -3506,41 +3507,25 @@ button:focus-visible, input:focus-visible, select:focus-visible { display: flex; gap: 0.55rem; align-items: center; + width: 100%; } .bgd-bookmark-pick { - min-width: min(34rem, 100%); + flex: 1 1 28rem; + min-width: 16rem; +} +.bgd-add-row .status-input { + flex: 1 1 auto; } .bgd-bookmark-list { - display: flex; - flex-wrap: wrap; - gap: 0.55rem; + min-height: 1.8rem; } -.bgd-bookmark-chip { - display: inline-flex; - align-items: center; - gap: 0.45rem; - padding: 0.35rem 0.55rem; - border-radius: 999px; - border: 1px solid var(--border-light); - background: var(--btn-bg); - color: var(--text); - font-size: 0.85rem; +.bgd-bookmark-tag { + padding-right: 0.3rem; } -.bgd-bookmark-chip-meta { +.bgd-bookmark-meta { color: var(--text-muted); font-size: 0.78rem; } -.bgd-bookmark-chip-remove { - border: none; - background: transparent; - color: var(--text-muted); - cursor: pointer; - padding: 0; - height: auto; -} -.bgd-bookmark-chip-remove:hover { - color: var(--text); -} .bgd-status-list { display: grid; gap: 0.65rem; @@ -3572,7 +3557,9 @@ button:focus-visible, input:focus-visible, select:focus-visible { } .bgd-status-state[data-state="out_of_span"], .bgd-status-state[data-state="waiting_for_spectrum"], -.bgd-status-state[data-state="inactive"] { +.bgd-status-state[data-state="waiting_for_user"], +.bgd-status-state[data-state="inactive"], +.bgd-status-state[data-state="handled_by_scheduler"] { color: var(--accent-yellow); } .bgd-status-state[data-state="missing_bookmark"], @@ -3591,4 +3578,7 @@ button:focus-visible, input:focus-visible, select:focus-visible { flex-direction: column; align-items: stretch; } + .bgd-add-row button { + width: 100%; + } } diff --git a/src/trx-client/trx-frontend/trx-frontend-http/src/background_decode.rs b/src/trx-client/trx-frontend/trx-frontend-http/src/background_decode.rs index 363df8d..8063012 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/src/background_decode.rs +++ b/src/trx-client/trx-frontend/trx-frontend-http/src/background_decode.rs @@ -4,6 +4,7 @@ use std::collections::HashMap; use std::path::{Path, PathBuf}; +use std::sync::atomic::Ordering; use std::sync::{Arc, RwLock}; use std::time::Duration; @@ -17,6 +18,7 @@ use trx_frontend::{FrontendRuntimeContext, SharedSpectrum, VChanAudioCmd}; use uuid::Uuid; use crate::server::bookmarks::{Bookmark, BookmarkStore}; +use crate::server::scheduler::SchedulerStatusMap; const SUPPORTED_DECODER_KINDS: &[&str] = &["ft8", "wspr", "hf-aprs"]; const CHANNEL_KIND_NAME: &str = "VirtualBackgroundDecodeChannel"; @@ -119,6 +121,7 @@ pub struct BackgroundDecodeManager { store: Arc, bookmarks: Arc, context: Arc, + scheduler_status: SchedulerStatusMap, status: Arc>>, notify_tx: broadcast::Sender<()>, } @@ -128,12 +131,14 @@ impl BackgroundDecodeManager { store: Arc, bookmarks: Arc, context: Arc, + scheduler_status: SchedulerStatusMap, ) -> Arc { let (notify_tx, _) = broadcast::channel(16); Arc::new(Self { store, bookmarks, context, + scheduler_status, status: Arc::new(RwLock::new(HashMap::new())), notify_tx, }) @@ -296,6 +301,12 @@ impl BackgroundDecodeManager { let config = self.get_config(&rig_id); let selected = dedup_ids(&config.bookmark_ids); + let users_connected = self.context.sse_clients.load(Ordering::Relaxed) > 0; + let scheduled_bookmark_ids = if users_connected { + Vec::new() + } else { + self.scheduler_bookmark_ids(&rig_id) + }; let selected_bookmarks: HashMap = self .bookmarks .list() @@ -344,6 +355,18 @@ impl BackgroundDecodeManager { continue; } + if !users_connected { + status.state = "waiting_for_user".to_string(); + statuses.push(status); + continue; + } + + if scheduled_bookmark_ids.iter().any(|id| id == &bookmark.id) { + status.state = "handled_by_scheduler".to_string(); + statuses.push(status); + continue; + } + let (Some(center_hz), Some(half_span_hz)) = (center_hz, half_span_hz) else { status.state = "waiting_for_spectrum".to_string(); statuses.push(status); @@ -409,6 +432,28 @@ impl BackgroundDecodeManager { } } + fn scheduler_bookmark_ids(&self, rig_id: &str) -> Vec { + let Ok(guard) = self.scheduler_status.read() else { + return Vec::new(); + }; + let Some(status) = guard.get(rig_id) else { + return Vec::new(); + }; + if !status.active { + return Vec::new(); + } + let mut out = Vec::new(); + if let Some(id) = status.last_bookmark_id.clone() { + out.push(id); + } + for id in &status.last_bookmark_ids { + if !out.iter().any(|existing| existing == id) { + out.push(id.clone()); + } + } + out + } + async fn run(self: Arc) { let mut runtime = BackgroundRuntimeState::default(); let mut notify_rx = self.notify_tx.subscribe(); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/src/server.rs b/src/trx-client/trx-frontend/trx-frontend-http/src/server.rs index 542a46c..d3be641 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/src/server.rs +++ b/src/trx-client/trx-frontend/trx-frontend-http/src/server.rs @@ -92,6 +92,7 @@ async fn serve( background_decode_store, bookmark_store.clone(), context.clone(), + scheduler_status.clone(), ); background_decode_mgr.spawn();