From 8c5706f6c35fd76bcb3eeb1a1eafd3d32dfdb37c Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 29 Mar 2026 22:27:21 +0000 Subject: [PATCH] [feat](trx-client): add bandplan display config to client settings Add bandplan_enabled (default: true) and bandplan_region (default: "iaru_r1") fields to [frontends.http] config section, allowing the operator to control the initial bandplan display setting from the server config rather than requiring each browser session to configure it manually. The server-provided default is applied on first connect only when the user has no existing localStorage override. https://claude.ai/code/session_01H7427hzbJepJzkoUJzoDmH Signed-off-by: Claude --- src/trx-client/src/config.rs | 50 +++++++++++++++++++ src/trx-client/src/main.rs | 2 + src/trx-client/trx-frontend/src/lib.rs | 4 ++ .../trx-frontend-http/assets/web/app.js | 17 +++++++ .../trx-frontend-http/src/api/mod.rs | 4 ++ 5 files changed, 77 insertions(+) diff --git a/src/trx-client/src/config.rs b/src/trx-client/src/config.rs index b37eb83..a3662ac 100644 --- a/src/trx-client/src/config.rs +++ b/src/trx-client/src/config.rs @@ -289,6 +289,10 @@ pub struct HttpFrontendConfig { pub spectrum_usable_span_ratio: f32, /// Whether to expose the RF Gain control in the web UI. pub show_sdr_gain_control: bool, + /// Whether the bandplan strip is shown by default in the spectrum display. + pub bandplan_enabled: bool, + /// Default bandplan region: "iaru_r1", "iaru_r2", or "iaru_r3". + pub bandplan_region: String, /// Default decode history retention in minutes for the active rig. pub decode_history_retention_min: u64, /// Optional per-rig decode history retention overrides in minutes. @@ -308,6 +312,8 @@ impl Default for HttpFrontendConfig { spectrum_coverage_margin_hz: 50_000, spectrum_usable_span_ratio: 0.92, show_sdr_gain_control: true, + bandplan_enabled: true, + bandplan_region: "iaru_r1".to_string(), decode_history_retention_min: 24 * 60, decode_history_retention_min_by_rig: HashMap::new(), auth: HttpAuthConfig::default(), @@ -507,6 +513,15 @@ impl ClientConfig { "[frontends.http].spectrum_usable_span_ratio must be > 0.0 and <= 1.0".to_string(), ); } + match self.frontends.http.bandplan_region.as_str() { + "iaru_r1" | "iaru_r2" | "iaru_r3" => {} + other => { + return Err(format!( + "[frontends.http].bandplan_region must be \"iaru_r1\", \"iaru_r2\", or \"iaru_r3\", got \"{}\"", + other + )); + } + } if self.frontends.http.decode_history_retention_min == 0 { return Err("[frontends.http].decode_history_retention_min must be > 0".to_string()); } @@ -650,6 +665,8 @@ impl ClientConfig { spectrum_coverage_margin_hz: 50_000, spectrum_usable_span_ratio: 0.92, show_sdr_gain_control: true, + bandplan_enabled: true, + bandplan_region: "iaru_r1".to_string(), decode_history_retention_min: 24 * 60, decode_history_retention_min_by_rig: HashMap::new(), auth: HttpAuthConfig { @@ -731,6 +748,8 @@ mod tests { assert_eq!(config.frontends.http.initial_map_zoom, 10); assert_eq!(config.frontends.http.spectrum_coverage_margin_hz, 50_000); assert_eq!(config.frontends.http.spectrum_usable_span_ratio, 0.92); + assert!(config.frontends.http.bandplan_enabled); + assert_eq!(config.frontends.http.bandplan_region, "iaru_r1"); assert_eq!(config.frontends.http.decode_history_retention_min, 1440); assert!(config .frontends @@ -807,6 +826,9 @@ uhf = 60 assert_eq!(config.frontends.http.initial_map_zoom, 12); assert_eq!(config.frontends.http.spectrum_coverage_margin_hz, 40_000); assert_eq!(config.frontends.http.spectrum_usable_span_ratio, 0.9); + // bandplan fields not set in TOML → defaults + assert!(config.frontends.http.bandplan_enabled); + assert_eq!(config.frontends.http.bandplan_region, "iaru_r1"); assert_eq!(config.frontends.http.decode_history_retention_min, 720); assert_eq!( config @@ -1155,4 +1177,32 @@ url = "remote.example.com:4530" .unwrap_err() .contains("poll_interval_ms must be > 0")); } + + #[test] + fn test_validate_rejects_invalid_bandplan_region() { + let mut config = ClientConfig::default(); + config.frontends.http.bandplan_region = "invalid".to_string(); + assert!(config.validate().is_err()); + } + + #[test] + fn test_validate_accepts_all_bandplan_regions() { + for region in &["iaru_r1", "iaru_r2", "iaru_r3"] { + let mut config = ClientConfig::default(); + config.frontends.http.bandplan_region = region.to_string(); + assert!(config.validate().is_ok(), "region {} should be valid", region); + } + } + + #[test] + fn test_parse_bandplan_config_from_toml() { + let toml_str = r#" +[frontends.http] +bandplan_enabled = false +bandplan_region = "iaru_r2" +"#; + let config: ClientConfig = toml::from_str(toml_str).unwrap(); + assert!(!config.frontends.http.bandplan_enabled); + assert_eq!(config.frontends.http.bandplan_region, "iaru_r2"); + } } diff --git a/src/trx-client/src/main.rs b/src/trx-client/src/main.rs index ae04924..8fa34a2 100644 --- a/src/trx-client/src/main.rs +++ b/src/trx-client/src/main.rs @@ -180,6 +180,8 @@ async fn async_init() -> DynResult { cfg.frontends.http.spectrum_coverage_margin_hz; frontend_runtime.http_ui.spectrum_usable_span_ratio = cfg.frontends.http.spectrum_usable_span_ratio; + frontend_runtime.http_ui.bandplan_enabled = cfg.frontends.http.bandplan_enabled; + frontend_runtime.http_ui.bandplan_region = cfg.frontends.http.bandplan_region.clone(); frontend_runtime.http_ui.decode_history_retention_min = cfg.frontends.http.decode_history_retention_min; frontend_runtime.http_ui.decode_history_retention_min_by_rig = cfg diff --git a/src/trx-client/trx-frontend/src/lib.rs b/src/trx-client/trx-frontend/src/lib.rs index b615155..98204ad 100644 --- a/src/trx-client/trx-frontend/src/lib.rs +++ b/src/trx-client/trx-frontend/src/lib.rs @@ -282,6 +282,8 @@ pub struct HttpUiConfig { pub initial_map_zoom: u8, pub spectrum_coverage_margin_hz: u32, pub spectrum_usable_span_ratio: f32, + pub bandplan_enabled: bool, + pub bandplan_region: String, pub decode_history_retention_min: u64, pub decode_history_retention_min_by_rig: HashMap, } @@ -293,6 +295,8 @@ impl Default for HttpUiConfig { initial_map_zoom: 10, spectrum_coverage_margin_hz: 50_000, spectrum_usable_span_ratio: 0.92, + bandplan_enabled: true, + bandplan_region: "iaru_r1".to_string(), decode_history_retention_min: 24 * 60, decode_history_retention_min_by_rig: HashMap::new(), } 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 48a578b..f291ecc 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 @@ -3177,6 +3177,22 @@ function render(update) { if (typeof update.show_sdr_gain_control === "boolean") { if (sdrSettingsRowEl) sdrSettingsRowEl.style.display = update.show_sdr_gain_control ? "" : "none"; } + // Apply server-configured bandplan defaults once, only when the user has not + // previously overridden the setting via the UI (localStorage). + if (!_bandplanServerDefaultApplied && typeof update.bandplan_enabled === "boolean" + && typeof update.bandplan_region === "string") { + _bandplanServerDefaultApplied = true; + const hasUserOverride = localStorage.getItem(STORAGE_PREFIX + "bandplanRegion") !== null; + if (!hasUserOverride) { + const region = update.bandplan_enabled ? update.bandplan_region : "off"; + bandplanRegion = region; + saveSetting("bandplanRegion", region); + if (bandplanRegionSelect) bandplanRegionSelect.value = region; + bandplanSegmentsCache = null; + bandplanCacheKey = ""; + if (lastSpectrumData) scheduleSpectrumDraw(); + } + } if (update.filter && sdrAgcEl && typeof update.filter.sdr_agc_enabled === "boolean") { sdrAgcEl.checked = update.filter.sdr_agc_enabled; updateSdrGainInputState(); @@ -11279,6 +11295,7 @@ if (spectrumCenterRightBtn) { let bandplanData = null; let bandplanRegion = loadSetting("bandplanRegion", "off"); let bandplanShowLabels = loadSetting("bandplanLabels", true); +let _bandplanServerDefaultApplied = false; let bandplanSegmentsCache = null; let bandplanCacheKey = ""; diff --git a/src/trx-client/trx-frontend/trx-frontend-http/src/api/mod.rs b/src/trx-client/trx-frontend/trx-frontend-http/src/api/mod.rs index a9eabe6..66d50f4 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/src/api/mod.rs +++ b/src/trx-client/trx-frontend/trx-frontend-http/src/api/mod.rs @@ -82,6 +82,8 @@ struct FrontendMeta { initial_map_zoom: u8, spectrum_coverage_margin_hz: u32, spectrum_usable_span_ratio: f32, + bandplan_enabled: bool, + bandplan_region: String, decode_history_retention_min: u64, server_connected: bool, } @@ -171,6 +173,8 @@ fn frontend_meta_from_context( initial_map_zoom: initial_map_zoom_from_context(context), spectrum_coverage_margin_hz: spectrum_coverage_margin_hz_from_context(context), spectrum_usable_span_ratio: spectrum_usable_span_ratio_from_context(context), + bandplan_enabled: context.http_ui.bandplan_enabled, + bandplan_region: context.http_ui.bandplan_region.clone(), decode_history_retention_min: decode_history_retention_min_from_context(context), server_connected, }