[fix](trx-frontend): refine spectrum layout behavior

Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
2026-03-02 20:36:22 +01:00
parent 89e44f3fa7
commit 518e10c36a
2 changed files with 75 additions and 27 deletions
@@ -289,6 +289,7 @@ function applyCapabilities(caps) {
if (centerFreqField) centerFreqField.style.display = "none";
stopSpectrumStreaming();
}
scheduleSpectrumLayout();
}
}
@@ -1653,6 +1654,8 @@ let serverLon = null;
let initialMapZoom = 10;
let spectrumCoverageMarginHz = 50_000;
let spectrumUsableSpanRatio = 0.92;
const DEFAULT_SPECTRUM_PLOT_HEIGHT_PX = 160;
let spectrumLayoutPending = false;
function updateFooterBuildInfo() {
const serverEl = document.getElementById("footer-server-build");
@@ -1662,6 +1665,56 @@ function updateFooterBuildInfo() {
serverEl.textContent = `trx-server v${ver} ${build}`;
}
function scheduleSpectrumLayout() {
if (spectrumLayoutPending) return;
spectrumLayoutPending = true;
requestAnimationFrame(() => {
spectrumLayoutPending = false;
updateSpectrumAutoHeight();
});
}
function updateSpectrumAutoHeight() {
const root = document.documentElement;
const tabMainEl = document.getElementById("tab-main");
const contentEl = document.getElementById("content");
const spectrumPanelEl = document.getElementById("spectrum-panel");
const spectrumCanvasEl = document.getElementById("spectrum-canvas");
if (!root || !tabMainEl || !contentEl || !spectrumPanelEl || !spectrumCanvasEl) return;
const mainVisible = getComputedStyle(tabMainEl).display !== "none";
const contentVisible = getComputedStyle(contentEl).display !== "none";
const spectrumVisible = getComputedStyle(spectrumPanelEl).display !== "none";
const currentHeight = Math.max(
DEFAULT_SPECTRUM_PLOT_HEIGHT_PX,
Math.round(spectrumCanvasEl.clientHeight || DEFAULT_SPECTRUM_PLOT_HEIGHT_PX),
);
if (!mainVisible || !contentVisible || !spectrumVisible) {
root.style.setProperty("--spectrum-plot-height", `${DEFAULT_SPECTRUM_PLOT_HEIGHT_PX}px`);
if (currentHeight !== DEFAULT_SPECTRUM_PLOT_HEIGHT_PX && lastSpectrumData) {
scheduleSpectrumDraw();
scheduleOverviewDraw();
}
return;
}
const tabBottom = tabMainEl.getBoundingClientRect().bottom;
const contentBottom = contentEl.getBoundingClientRect().bottom;
const slackPx = Math.floor(tabBottom - contentBottom);
const nextHeight = Math.max(
DEFAULT_SPECTRUM_PLOT_HEIGHT_PX,
currentHeight + slackPx - 2,
);
if (Math.abs(nextHeight - currentHeight) < 2) return;
root.style.setProperty("--spectrum-plot-height", `${nextHeight}px`);
if (lastSpectrumData) {
scheduleSpectrumDraw();
scheduleOverviewDraw();
}
}
function updateTitle() {
const titleEl = document.getElementById("rig-title");
if (titleEl) {
@@ -1716,6 +1769,7 @@ function render(update) {
) {
spectrumUsableSpanRatio = Math.max(0.01, Math.min(1.0, Number(update.spectrum_usable_span_ratio)));
}
scheduleSpectrumLayout();
updateTitle();
updateFooterBuildInfo();
@@ -2719,12 +2773,14 @@ document.querySelector(".tab-bar").addEventListener("click", (e) => {
btn.classList.add("active");
document.querySelectorAll(".tab-panel").forEach((p) => p.style.display = "none");
document.getElementById(`tab-${btn.dataset.tab}`).style.display = "";
scheduleSpectrumLayout();
if (btn.dataset.tab === "map") {
initAprsMap();
sizeAprsMapToViewport();
if (aprsMap) setTimeout(() => aprsMap.invalidateSize(), 50);
}
});
window.addEventListener("resize", () => { scheduleSpectrumLayout(); });
// --- Auth startup sequence ---
async function initializeApp() {
@@ -4878,16 +4934,24 @@ function createBookmarkChip(bm, colorMap) {
function updateSideBookmarkStack(container, bookmarks, colorMap) {
if (!container) return;
container.innerHTML = "";
const nextKey = Array.isArray(bookmarks) ? bookmarks.map((bm) => bm.id).join(",") : "";
if (!Array.isArray(bookmarks) || bookmarks.length === 0) {
if (container.dataset.bmKey) {
container.innerHTML = "";
container.dataset.bmKey = "";
}
container.classList.remove("bm-side-visible");
return;
}
container.classList.add("bm-side-visible");
for (const bm of bookmarks) {
container.appendChild(createBookmarkChip(bm, colorMap));
if (container.dataset.bmKey !== nextKey) {
container.dataset.bmKey = nextKey;
container.innerHTML = "";
for (const bm of bookmarks) {
container.appendChild(createBookmarkChip(bm, colorMap));
}
}
container.classList.add("bm-side-visible");
}
function updateBookmarkAxis(range) {
@@ -26,6 +26,7 @@
--filter-border: #385577;
--wavelength-fg: #8da3be;
--spectrum-bg: #0a0f18;
--overview-plot-height: 160px;
--spectrum-plot-height: 160px;
--jog-wheel-size: 83.2px;
--header-waterfall-overlap: 0rem;
@@ -525,10 +526,8 @@ small { color: var(--text-muted); }
position: relative;
display: flex;
flex-direction: column;
flex: 1 1 auto;
min-height: 0;
gap: 0;
margin-bottom: 0;
margin-bottom: 0.9rem;
}
#signal-overlay-canvas {
position: absolute;
@@ -833,7 +832,7 @@ small { color: var(--text-muted); }
}
#overview-canvas {
width: 100%;
height: var(--spectrum-plot-height);
height: var(--overview-plot-height);
display: block;
}
.header-left {
@@ -914,12 +913,7 @@ small { color: var(--text-muted); }
.meter-bar { flex: 1 1 auto; height: 12px; border-radius: 999px; background: var(--btn-bg); border: 1px solid var(--border-light); overflow: hidden; }
.meter-fill { height: 100%; width: 0%; background: linear-gradient(90deg, var(--accent-green), var(--accent-yellow), var(--accent-red)); transition: width 150ms ease; }
.meter-value { font-size: 0.95rem; color: var(--text-heading); min-width: 64px; text-align: right; }
#tab-main {
display: flex;
flex-direction: column;
min-height: 0;
}
#content { display: flex; flex: 1 1 auto; flex-direction: column; gap: 1.1rem; min-height: 0; overflow: visible; }
#content { display: flex; flex-direction: column; gap: 1.1rem; min-height: 0; overflow: visible; }
.tab-panel { flex: 1 1 auto; min-height: 0; overflow: visible; }
.tab-bar {
display: flex;
@@ -1514,26 +1508,16 @@ button:focus-visible, input:focus-visible, select:focus-visible {
/* ── Spectrum display ─────────────────────────────────────────────────── */
#spectrum-panel {
margin-bottom: 0;
flex: 1 1 auto;
min-height: 0;
display: flex;
flex-direction: column;
}
.spectrum-wrap {
position: relative;
display: flex;
flex: 1 1 auto;
flex-direction: column;
min-height: 0;
width: 100%;
user-select: none;
}
#spectrum-canvas {
display: block;
width: 100%;
flex: 1 1 auto;
min-height: var(--spectrum-plot-height);
height: auto;
height: var(--spectrum-plot-height);
background: var(--spectrum-bg);
border-radius: 6px 6px 0 0;
cursor: crosshair;
@@ -1541,7 +1525,7 @@ button:focus-visible, input:focus-visible, select:focus-visible {
}
.spectrum-edge-shift {
position: absolute;
top: calc((100% - 18px) / 2);
top: calc(var(--spectrum-plot-height) / 2);
transform: translateY(-50%);
z-index: 8;
width: 1.7rem;
@@ -1646,7 +1630,7 @@ button:focus-visible, input:focus-visible, select:focus-visible {
}
.spectrum-bookmark-side {
position: absolute;
top: calc((100% - 18px) / 2);
top: calc(var(--spectrum-plot-height) / 2);
transform: translateY(-50%);
z-index: 7;
width: 7.25rem;