From 6f53592041e7eda92cbd46e7005b992d55704d61 Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Mon, 3 Aug 2026 20:37:12 +0200 Subject: [PATCH] [feat](trx-frontend-http): rework the page footer The footer floated in space below the content with no rule to close the page, its two clusters sat on a text baseline that left the source pill hanging, and the status hint was a plain line of text a size larger than the attribution beside it. Now a hairline closes the page the way .tab-bar opens it, the clusters centre on one line, and the attribution drops the opacity it stacked on top of --text-muted, which had put it below a readable contrast ratio. The status hint becomes a pill with a state dot: green when ready, amber while a command is in flight, red on connection loss. The colour comes from a data-state attribute, so every hint now goes through setPowerHint instead of assigning textContent directly. --status-ok carries the indicator green; .about-status-on picks it up too, which darkens it on light themes where the old value was barely legible. Signed-off-by: Stan Grams --- .../assets/web/generated/app.js | 33 ++++--- .../trx-frontend-http/assets/web/index.html | 9 +- .../trx-frontend-http/assets/web/style.css | 86 ++++++++++++++++++- .../trx-frontend-http/frontend/src/app.ts | 38 +++++--- .../frontend/tests/browser-smoke.mjs | 9 ++ 5 files changed, 147 insertions(+), 28 deletions(-) diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/app.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/app.js index 7f15e6ab..ee2798ca 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/app.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/generated/app.js @@ -2724,6 +2724,17 @@ if (headerStylePickSelect) { function readyText() { return lastClientCount !== null ? `Ready · ${lastClientCount} user${lastClientCount !== 1 ? "s" : ""}` : "Ready"; } +var HINT_ERROR_RE = /failed|missing|unavailable|unknown|lost|required/i; +var HINT_BUSY_RE = /initializ|connecting|retrying|scanning|shifting|waiting|sending|switching|toggling|setting|not fully/i; +function hintState(msg) { + if (HINT_ERROR_RE.test(msg)) return "error"; + if (HINT_BUSY_RE.test(msg) || /[\u2026]$|\.\.\.$/.test(msg)) return "busy"; + return "ok"; +} +function setPowerHint(msg) { + powerHint.textContent = msg; + powerHint.dataset.state = hintState(msg); +} function rigBadgeColor(rigId) { const text = (rigId || "rx").toString(); let hash = 0; @@ -2837,12 +2848,12 @@ function refreshOperatorLayoutCapabilities() { }); } function showHint(msg, duration) { - powerHint.textContent = msg; + setPowerHint(msg); if (hintTimer) clearTimeout(hintTimer); if (duration) hintTimer = setTimeout(() => { - powerHint.textContent = readyText(); + setPowerHint(readyText()); }, duration); - if (/failed|missing|unavailable|unknown|lost|required/i.test(msg)) { + if (HINT_ERROR_RE.test(msg)) { window.trxUi?.notify(msg, { kind: "error" }); } } @@ -4405,13 +4416,13 @@ function render(update) { console.info("Rig initializing:", { manufacturer: manu, model, revision: rev }); loadingEl.style.display = ""; if (contentEl) contentEl.style.display = "none"; - powerHint.textContent = "Initializing rig…"; + setPowerHint("Initializing rig…"); setDisabled(true); return; } loadingEl.style.display = "none"; if (contentEl) contentEl.style.display = ""; - powerHint.textContent = "Rig not fully initialized yet"; + setPowerHint("Rig not fully initialized yet"); } else { loadingEl.style.display = "none"; if (contentEl) contentEl.style.display = ""; @@ -4730,7 +4741,7 @@ function render(update) { powerBtn.disabled = true; powerBtn.textContent = "Power unavailable"; powerBtn.setAttribute("aria-pressed", "false"); - powerHint.textContent = "State unknown"; + setPowerHint("State unknown"); } if (update.status && update.status.tx && typeof update.status.tx.limit === "number") { txLimitInput.value = String(update.status.tx.limit); @@ -4827,7 +4838,7 @@ function render(update) { if (Array.isArray(update.remotes)) { applyRigList(typeof update.active_remote === "string" ? update.active_remote : null, update.remotes); } - powerHint.textContent = readyText(); + setPowerHint(readyText()); lastLocked = update.status?.lock === true; window.trxUi?.setButtonState(lockBtn, { active: lastLocked, @@ -4905,11 +4916,11 @@ function connect() { render(data); lastEventAt = Date.now(); if (data.server_connected === false) { - powerHint.textContent = "trx-server connection lost"; + setPowerHint("trx-server connection lost"); if (tabMainEl) tabMainEl.classList.add("server-disconnected"); } else { if (tabMainEl) tabMainEl.classList.remove("server-disconnected"); - if (data.initialized) powerHint.textContent = readyText(); + if (data.initialized) setPowerHint(readyText()); } } catch (e) { console.error("Bad event data", e); @@ -4932,7 +4943,7 @@ function connect() { }); source.onerror = () => { if (source.readyState === EventSource.CLOSED) { - powerHint.textContent = "trx-client connection lost, retrying…"; + setPowerHint("trx-client connection lost, retrying…"); setConnLostOverlay(true, "trx-client connection lost", "Retrying…", true); source.close(); void pollFreshSnapshot(); @@ -4942,7 +4953,7 @@ function connect() { esHeartbeat = setInterval(() => { const now = Date.now(); if (now - lastEventAt > 15e3) { - powerHint.textContent = "trx-client connection lost, retrying…"; + setPowerHint("trx-client connection lost, retrying…"); setConnLostOverlay(true, "trx-client connection lost", "Retrying…", true); source.close(); void pollFreshSnapshot(); 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 4dbdc8f9..0a2e96ac 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 @@ -1594,10 +1594,13 @@ SPDX-License-Identifier: GPL-2.0-or-later