From f537e1a11b28c1afbbf9fa368c74e51cc521194b Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Sun, 1 Mar 2026 16:35:12 +0100 Subject: [PATCH] [feat](trx-client): add configurable initial map zoom Add an HTTP frontend config option for the initial APRS map zoom, expose it through frontend metadata, and apply it in the web UI. Co-authored-by: OpenAI Codex Signed-off-by: Stan Grams --- src/trx-client/src/config.rs | 10 ++++++++++ src/trx-client/src/main.rs | 1 + src/trx-client/trx-frontend/src/lib.rs | 3 +++ .../trx-frontend/trx-frontend-http/assets/web/app.js | 6 +++++- .../trx-frontend/trx-frontend-http/src/api.rs | 10 ++++++++++ 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/trx-client/src/config.rs b/src/trx-client/src/config.rs index 3c510b6..10fe21e 100644 --- a/src/trx-client/src/config.rs +++ b/src/trx-client/src/config.rs @@ -230,6 +230,8 @@ pub struct HttpFrontendConfig { pub port: u16, /// Default rig selected in the web UI on startup. pub default_rig_id: Option, + /// Initial zoom level for the APRS map when receiver coordinates are known. + pub initial_map_zoom: u8, /// Whether to expose the RF Gain control in the web UI. pub show_sdr_gain_control: bool, /// Authentication settings @@ -243,6 +245,7 @@ impl Default for HttpFrontendConfig { listen: IpAddr::from([127, 0, 0, 1]), port: 8080, default_rig_id: None, + initial_map_zoom: 10, show_sdr_gain_control: true, auth: HttpAuthConfig::default(), } @@ -342,6 +345,9 @@ impl ClientConfig { ); } } + if self.frontends.http.initial_map_zoom == 0 { + return Err("[frontends.http].initial_map_zoom must be > 0".to_string()); + } if self.frontends.rigctl.enabled && self.frontends.rigctl.rig_ports.is_empty() { return Err( "[frontends.rigctl].rig_ports must contain at least one rig when enabled" @@ -434,6 +440,7 @@ impl ClientConfig { listen: IpAddr::from([127, 0, 0, 1]), port: 8080, default_rig_id: Some("hf".to_string()), + initial_map_zoom: 10, show_sdr_gain_control: true, auth: HttpAuthConfig { enabled: false, @@ -533,6 +540,7 @@ mod tests { assert!(config.frontends.http.enabled); assert!(!config.frontends.rigctl.enabled); assert_eq!(config.frontends.http.port, 8080); + assert_eq!(config.frontends.http.initial_map_zoom, 10); assert_eq!(config.frontends.rigctl.port, 4532); assert!(config.frontends.http_json.enabled); assert_eq!(config.frontends.http_json.port, 0); @@ -562,6 +570,7 @@ poll_interval_ms = 500 enabled = true listen = "127.0.0.1" port = 8080 +initial_map_zoom = 12 "#; @@ -572,6 +581,7 @@ port = 8080 assert_eq!(config.remote.auth.token, Some("my-token".to_string())); assert_eq!(config.remote.poll_interval_ms, 500); assert!(config.frontends.http.enabled); + assert_eq!(config.frontends.http.initial_map_zoom, 12); } #[test] diff --git a/src/trx-client/src/main.rs b/src/trx-client/src/main.rs index 236ae67..742c70a 100644 --- a/src/trx-client/src/main.rs +++ b/src/trx-client/src/main.rs @@ -178,6 +178,7 @@ async fn async_init() -> DynResult { config::CookieSameSite::None => "None".to_string(), }; frontend_runtime.http_show_sdr_gain_control = cfg.frontends.http.show_sdr_gain_control; + frontend_runtime.http_initial_map_zoom = cfg.frontends.http.initial_map_zoom; // Resolve remote URL: CLI > config [remote] section > error let remote_url = cli diff --git a/src/trx-client/trx-frontend/src/lib.rs b/src/trx-client/trx-frontend/src/lib.rs index 7107dcb..95cb304 100644 --- a/src/trx-client/trx-frontend/src/lib.rs +++ b/src/trx-client/trx-frontend/src/lib.rs @@ -168,6 +168,8 @@ pub struct FrontendRuntimeContext { pub http_auth_cookie_same_site: String, /// Whether the HTTP UI should expose the RF Gain control. pub http_show_sdr_gain_control: bool, + /// Initial APRS map zoom level when receiver coordinates are available. + pub http_initial_map_zoom: u8, /// Currently selected remote rig id (used by remote client routing). pub remote_active_rig_id: Arc>>, /// Cached remote rig list from GetRigs polling. @@ -202,6 +204,7 @@ impl FrontendRuntimeContext { http_auth_cookie_secure: false, http_auth_cookie_same_site: "Lax".to_string(), http_show_sdr_gain_control: true, + http_initial_map_zoom: 10, remote_active_rig_id: Arc::new(Mutex::new(None)), remote_rigs: Arc::new(Mutex::new(Vec::new())), owner_callsign: None, 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 29054d7..f6ad49a 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 @@ -1247,6 +1247,7 @@ let serverCallsign = null; let ownerCallsign = null; let serverLat = null; let serverLon = null; +let initialMapZoom = 10; function updateFooterBuildInfo() { const serverEl = document.getElementById("footer-server-build"); @@ -1274,6 +1275,9 @@ function render(update) { } if (update.server_latitude != null) serverLat = update.server_latitude; if (update.server_longitude != null) serverLon = update.server_longitude; + if (typeof update.initial_map_zoom === "number" && Number.isFinite(update.initial_map_zoom)) { + initialMapZoom = Math.max(1, Math.round(update.initial_map_zoom)); + } updateTitle(); updateFooterBuildInfo(); @@ -2406,7 +2410,7 @@ function initAprsMap() { const hasLocation = serverLat != null && serverLon != null; const center = hasLocation ? [serverLat, serverLon] : [20, 0]; - const zoom = hasLocation ? 10 : 2; + const zoom = hasLocation ? initialMapZoom : 2; aprsMap = L.map("aprs-map").setView(center, zoom); updateMapBaseLayerForTheme(currentTheme()); diff --git a/src/trx-client/trx-frontend/trx-frontend-http/src/api.rs b/src/trx-client/trx-frontend/trx-frontend-http/src/api.rs index 4744565..3aad0c5 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/src/api.rs +++ b/src/trx-client/trx-frontend/trx-frontend-http/src/api.rs @@ -37,6 +37,7 @@ struct FrontendMeta { rig_ids: Vec, owner_callsign: Option, show_sdr_gain_control: bool, + initial_map_zoom: u8, } #[get("/status")] @@ -85,6 +86,10 @@ fn inject_frontend_meta(json: &str, meta: FrontendMeta) -> String { "show_sdr_gain_control".to_string(), serde_json::json!(meta.show_sdr_gain_control), ); + map.insert( + "initial_map_zoom".to_string(), + serde_json::json!(meta.initial_map_zoom), + ); serde_json::to_string(&value).unwrap_or_else(|_| json.to_string()) } @@ -101,6 +106,7 @@ fn frontend_meta_from_context( rig_ids: rig_ids_from_context(context), owner_callsign: owner_callsign_from_context(context), show_sdr_gain_control: show_sdr_gain_control_from_context(context), + initial_map_zoom: initial_map_zoom_from_context(context), } } @@ -138,6 +144,10 @@ fn show_sdr_gain_control_from_context(context: &FrontendRuntimeContext) -> bool context.http_show_sdr_gain_control } +fn initial_map_zoom_from_context(context: &FrontendRuntimeContext) -> u8 { + context.http_initial_map_zoom +} + #[get("/events")] pub async fn events( state: web::Data>,