From 00f5108a58ac4c2ff8ceedffaae6b89c9aa1422d Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Sun, 1 Mar 2026 18:19:36 +0100 Subject: [PATCH] [feat](trx-client): support website_name in header Add an optional website_name config field and prefer it over callsign for the linked web header title label. Co-authored-by: OpenAI Codex Signed-off-by: Stan Grams --- src/trx-client/src/config.rs | 12 ++++++++++++ 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 | 9 +++++++++ 5 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/trx-client/src/config.rs b/src/trx-client/src/config.rs index 3d08496..fcaaa5c 100644 --- a/src/trx-client/src/config.rs +++ b/src/trx-client/src/config.rs @@ -39,6 +39,8 @@ pub struct GeneralConfig { pub callsign: Option, /// Optional website URL to use as the web UI header title link. pub website_url: Option, + /// Optional website name to use as the web UI header title label. + pub website_name: Option, /// Log level (trace, debug, info, warn, error) pub log_level: Option, } @@ -48,6 +50,7 @@ impl Default for GeneralConfig { Self { callsign: Some("N0CALL".to_string()), website_url: None, + website_name: None, log_level: None, } } @@ -342,6 +345,11 @@ impl ClientConfig { return Err("[general].website_url must not be empty when set".to_string()); } } + if let Some(name) = &self.general.website_name { + if name.trim().is_empty() { + return Err("[general].website_name must not be empty when set".to_string()); + } + } if self.frontends.http.enabled && self.frontends.http.port == 0 { return Err("[frontends.http].port must be > 0 when enabled".to_string()); @@ -433,6 +441,7 @@ impl ClientConfig { general: GeneralConfig { callsign: Some("N0CALL".to_string()), website_url: Some("https://haxx.space".to_string()), + website_name: Some("haxx.space".to_string()), log_level: Some("info".to_string()), }, remote: RemoteConfig { @@ -555,6 +564,7 @@ mod tests { assert_eq!(config.frontends.http_json.port, 0); assert!(config.remote.url.is_none()); assert!(config.general.website_url.is_none()); + assert!(config.general.website_name.is_none()); assert_eq!(config.remote.poll_interval_ms, 750); assert!(config.frontends.audio.enabled); assert_eq!(config.frontends.audio.server_port, 4531); @@ -570,6 +580,7 @@ mod tests { [general] callsign = "W1AW" website_url = "https://example.com" +website_name = "Example" [remote] url = "192.168.1.100:9000" @@ -588,6 +599,7 @@ initial_map_zoom = 12 let config: ClientConfig = toml::from_str(toml_str).unwrap(); assert_eq!(config.general.callsign, Some("W1AW".to_string())); assert_eq!(config.general.website_url, Some("https://example.com".to_string())); + assert_eq!(config.general.website_name, Some("Example".to_string())); assert_eq!(config.remote.url, Some("192.168.1.100:9000".to_string())); assert_eq!(config.remote.rig_id, Some("hf".to_string())); assert_eq!(config.remote.auth.token, Some("my-token".to_string())); diff --git a/src/trx-client/src/main.rs b/src/trx-client/src/main.rs index 6e2d8dc..bbaff86 100644 --- a/src/trx-client/src/main.rs +++ b/src/trx-client/src/main.rs @@ -245,6 +245,7 @@ async fn async_init() -> DynResult { .or_else(|| cfg.general.callsign.clone()); frontend_runtime.owner_callsign = callsign.clone(); frontend_runtime.owner_website_url = cfg.general.website_url.clone(); + frontend_runtime.owner_website_name = cfg.general.website_name.clone(); info!( "Starting trx-client (remote: {}, frontends: {})", diff --git a/src/trx-client/trx-frontend/src/lib.rs b/src/trx-client/trx-frontend/src/lib.rs index 15986d3..acb227f 100644 --- a/src/trx-client/trx-frontend/src/lib.rs +++ b/src/trx-client/trx-frontend/src/lib.rs @@ -178,6 +178,8 @@ pub struct FrontendRuntimeContext { pub owner_callsign: Option, /// Optional website URL for the web UI header title link. pub owner_website_url: Option, + /// Optional website name for the web UI header title label. + pub owner_website_name: Option, /// Latest spectrum frame from the active SDR rig; None for non-SDR backends. pub spectrum: Arc>, } @@ -211,6 +213,7 @@ impl FrontendRuntimeContext { remote_rigs: Arc::new(Mutex::new(Vec::new())), owner_callsign: None, owner_website_url: None, + owner_website_name: None, spectrum: Arc::new(Mutex::new(SharedSpectrum::default())), } } 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 4fe8ac5..3e63225 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 @@ -1266,6 +1266,7 @@ let serverBuildDate = null; let serverCallsign = null; let ownerCallsign = null; let ownerWebsiteUrl = null; +let ownerWebsiteName = null; let serverRigs = []; let serverActiveRigId = null; let serverLat = null; @@ -1284,7 +1285,7 @@ function updateTitle() { const titleEl = document.getElementById("rig-title"); if (titleEl) { if (ownerWebsiteUrl) { - const label = ownerCallsign || displayLabelFromUrl(ownerWebsiteUrl); + const label = ownerWebsiteName || displayLabelFromUrl(ownerWebsiteUrl); titleEl.innerHTML = `${escapeMapHtml(label)}`; } else { @@ -1314,6 +1315,9 @@ function render(update) { if (typeof update.owner_website_url === "string" && update.owner_website_url.length > 0) { ownerWebsiteUrl = update.owner_website_url; } + if (typeof update.owner_website_name === "string" && update.owner_website_name.length > 0) { + ownerWebsiteName = update.owner_website_name; + } 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)) { 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 cddffd7..e165023 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, owner_website_url: Option, + owner_website_name: Option, show_sdr_gain_control: bool, initial_map_zoom: u8, } @@ -86,6 +87,9 @@ fn inject_frontend_meta(json: &str, meta: FrontendMeta) -> String { if let Some(url) = meta.owner_website_url { map.insert("owner_website_url".to_string(), serde_json::json!(url)); } + if let Some(name) = meta.owner_website_name { + map.insert("owner_website_name".to_string(), serde_json::json!(name)); + } map.insert( "show_sdr_gain_control".to_string(), serde_json::json!(meta.show_sdr_gain_control), @@ -110,6 +114,7 @@ fn frontend_meta_from_context( rig_ids: rig_ids_from_context(context), owner_callsign: owner_callsign_from_context(context), owner_website_url: owner_website_url_from_context(context), + owner_website_name: owner_website_name_from_context(context), show_sdr_gain_control: show_sdr_gain_control_from_context(context), initial_map_zoom: initial_map_zoom_from_context(context), } @@ -149,6 +154,10 @@ fn owner_website_url_from_context(context: &FrontendRuntimeContext) -> Option Option { + context.owner_website_name.clone() +} + fn show_sdr_gain_control_from_context(context: &FrontendRuntimeContext) -> bool { context.http_show_sdr_gain_control }