[feat](trx-frontend-http): callsign link, centered freq, title, jog lock
- Make server callsign a clickable link to qrzcq.com - Center frequency input text and use DSEG14 Classic font - Include callsign in page title (e.g. N0CALL - trx-frontend-http v0.1.0) - Block jog wheel when rig lock is active Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
This commit is contained in:
@@ -36,6 +36,8 @@ let lastFreqHz = null;
|
|||||||
let jogStep = 1000; // default 1 kHz
|
let jogStep = 1000; // default 1 kHz
|
||||||
let jogAngle = 0;
|
let jogAngle = 0;
|
||||||
let lastClientCount = null;
|
let lastClientCount = null;
|
||||||
|
let lastLocked = false;
|
||||||
|
const originalTitle = document.title;
|
||||||
|
|
||||||
function readyText() {
|
function readyText() {
|
||||||
return lastClientCount !== null ? `Ready \u00b7 ${lastClientCount} user${lastClientCount !== 1 ? "s" : ""}` : "Ready";
|
return lastClientCount !== null ? `Ready \u00b7 ${lastClientCount} user${lastClientCount !== 1 ? "s" : ""}` : "Ready";
|
||||||
@@ -155,10 +157,15 @@ function render(update) {
|
|||||||
}
|
}
|
||||||
// Server subtitle: "trx-server vX.Y.Z hosted by CALL"
|
// Server subtitle: "trx-server vX.Y.Z hosted by CALL"
|
||||||
if (update.server_version || update.server_callsign) {
|
if (update.server_version || update.server_callsign) {
|
||||||
let text = "trx-server";
|
let parts = "trx-server";
|
||||||
if (update.server_version) text += ` v${update.server_version}`;
|
if (update.server_version) parts += ` v${update.server_version}`;
|
||||||
if (update.server_callsign) text += ` hosted by ${update.server_callsign}`;
|
if (update.server_callsign) {
|
||||||
serverSubtitle.textContent = text;
|
const cs = update.server_callsign;
|
||||||
|
serverSubtitle.innerHTML = `${parts} hosted by <a href="https://www.qrzcq.com/call/${encodeURIComponent(cs)}" target="_blank" rel="noopener">${cs}</a>`;
|
||||||
|
document.title = `${cs} - ${originalTitle}`;
|
||||||
|
} else {
|
||||||
|
serverSubtitle.textContent = parts;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setDisabled(false);
|
setDisabled(false);
|
||||||
if (update.info && update.info.capabilities && Array.isArray(update.info.capabilities.supported_modes)) {
|
if (update.info && update.info.capabilities && Array.isArray(update.info.capabilities.supported_modes)) {
|
||||||
@@ -280,8 +287,8 @@ function render(update) {
|
|||||||
|
|
||||||
if (typeof update.clients === "number") lastClientCount = update.clients;
|
if (typeof update.clients === "number") lastClientCount = update.clients;
|
||||||
powerHint.textContent = readyText();
|
powerHint.textContent = readyText();
|
||||||
const locked = update.status && update.status.lock === true;
|
lastLocked = update.status && update.status.lock === true;
|
||||||
lockBtn.textContent = locked ? "Unlock" : "Lock";
|
lockBtn.textContent = lastLocked ? "Unlock" : "Lock";
|
||||||
|
|
||||||
const tx = update.status && update.status.tx ? update.status.tx : null;
|
const tx = update.status && update.status.tx ? update.status.tx : null;
|
||||||
txMeters.style.display = "";
|
txMeters.style.display = "";
|
||||||
@@ -462,6 +469,7 @@ const jogUpBtn = document.getElementById("jog-up");
|
|||||||
const jogStepEl = document.getElementById("jog-step");
|
const jogStepEl = document.getElementById("jog-step");
|
||||||
|
|
||||||
async function jogFreq(direction) {
|
async function jogFreq(direction) {
|
||||||
|
if (lastLocked) { showHint("Locked", 1500); return; }
|
||||||
if (lastFreqHz === null) return;
|
if (lastFreqHz === null) return;
|
||||||
const newHz = lastFreqHz + direction * jogStep;
|
const newHz = lastFreqHz + direction * jogStep;
|
||||||
if (!freqAllowed(newHz)) {
|
if (!freqAllowed(newHz)) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>{pkg} v{ver} status</title>
|
<title>{pkg} v{ver}</title>
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/dseg14-classic/400.css" />
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/dseg14-classic/400.css" />
|
||||||
<link rel="stylesheet" href="/style.css" />
|
<link rel="stylesheet" href="/style.css" />
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ body { font-family: sans-serif; margin: 0; min-height: 100vh; display: flex; ali
|
|||||||
.label { color: var(--text-muted); font-size: 0.9rem; margin-bottom: 6px; display: block; }
|
.label { color: var(--text-muted); font-size: 0.9rem; margin-bottom: 6px; display: block; }
|
||||||
.status { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1.1rem 1rem; }
|
.status { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1.1rem 1rem; }
|
||||||
input.status-input, select.status-input { width: 100%; padding: 0.45rem 0.5rem; font-size: 1rem; border: 1px solid var(--border-light); border-radius: 6px; background: var(--input-bg); color: var(--text); }
|
input.status-input, select.status-input { width: 100%; padding: 0.45rem 0.5rem; font-size: 1rem; border: 1px solid var(--border-light); border-radius: 6px; background: var(--input-bg); color: var(--text); }
|
||||||
#freq { font-family: 'DSEG14 Classic', monospace; font-size: 2rem; padding: 0.5rem 0.6rem; letter-spacing: 0.05em; }
|
#freq { font-family: 'DSEG14 Classic', monospace; font-size: 2rem; padding: 0.5rem 0.6rem; letter-spacing: 0.05em; text-align: center; }
|
||||||
.controls-row {
|
.controls-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
@@ -140,6 +140,8 @@ small { color: var(--text-muted); }
|
|||||||
.logo-bg { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; pointer-events: none; opacity: 0.2; }
|
.logo-bg { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; pointer-events: none; opacity: 0.2; }
|
||||||
.logo-bg img { max-width: 50%; max-height: 50%; filter: drop-shadow(0 4px 12px rgba(0,0,0,0.35)); }
|
.logo-bg img { max-width: 50%; max-height: 50%; filter: drop-shadow(0 4px 12px rgba(0,0,0,0.35)); }
|
||||||
.subtitle { color: var(--text-muted); font-size: 0.95rem; }
|
.subtitle { color: var(--text-muted); font-size: 0.95rem; }
|
||||||
|
.subtitle a { color: var(--accent-green); text-decoration: none; }
|
||||||
|
.subtitle a:hover { text-decoration: underline; }
|
||||||
.band-tag { display: inline-block; padding: 2px 6px; border-radius: 6px; background: var(--btn-bg); color: var(--text); font-size: 0.82rem; border: 1px solid var(--border-light); margin-left: 6px; }
|
.band-tag { display: inline-block; padding: 2px 6px; border-radius: 6px; background: var(--btn-bg); color: var(--text); font-size: 0.82rem; border: 1px solid var(--border-light); margin-left: 6px; }
|
||||||
.signal { display: flex; gap: 0.6rem; align-items: center; }
|
.signal { display: flex; gap: 0.6rem; align-items: center; }
|
||||||
.signal-bar { flex: 1 1 auto; height: 12px; border-radius: 999px; background: var(--btn-bg); border: 1px solid var(--border-light); overflow: hidden; }
|
.signal-bar { flex: 1 1 auto; height: 12px; border-radius: 999px; background: var(--btn-bg); border: 1px solid var(--border-light); overflow: hidden; }
|
||||||
|
|||||||
Reference in New Issue
Block a user