[fix](trx-frontend): maximize spectrum viewport and add resize grip

Make spectrum plot use maximum available viewport height by default.
Add draggable vertical resize grip with a reasonable minimum height
and double-click reset back to auto-max.

Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
2026-03-05 17:45:50 +01:00
parent bfff095cf0
commit a905fa9678
3 changed files with 121 additions and 14 deletions
@@ -1932,7 +1932,10 @@ let spectrumCoverageMarginHz = 50_000;
let spectrumUsableSpanRatio = 0.92;
const DEFAULT_OVERVIEW_PLOT_HEIGHT_PX = 160;
const DEFAULT_SPECTRUM_PLOT_HEIGHT_PX = 160;
const MIN_SPECTRUM_PLOT_HEIGHT_PX = 130;
let spectrumLayoutPending = false;
let spectrumManualPlotHeightPx = null;
let spectrumResizeState = null;
function updateFooterBuildInfo() {
const serverEl = document.getElementById("footer-server-build");
@@ -1951,6 +1954,28 @@ function scheduleSpectrumLayout() {
});
}
function currentSpectrumHeightPx(spectrumCanvasEl) {
return Math.max(
MIN_SPECTRUM_PLOT_HEIGHT_PX,
Math.round(spectrumCanvasEl?.clientHeight || DEFAULT_SPECTRUM_PLOT_HEIGHT_PX),
);
}
function spectrumHeightBoundsPx(tabMainEl, contentEl, spectrumCanvasEl) {
const currentSpectrumHeight = currentSpectrumHeightPx(spectrumCanvasEl);
const tabBottom = tabMainEl.getBoundingClientRect().bottom;
const contentBottom = contentEl.getBoundingClientRect().bottom;
const slackPx = Math.floor(tabBottom - contentBottom);
const maxHeight = Math.max(
MIN_SPECTRUM_PLOT_HEIGHT_PX,
currentSpectrumHeight + slackPx - 2,
);
return {
min: MIN_SPECTRUM_PLOT_HEIGHT_PX,
max: maxHeight,
};
}
function updateSpectrumAutoHeight() {
const root = document.documentElement;
const tabMainEl = document.getElementById("tab-main");
@@ -1986,30 +2011,86 @@ function updateSpectrumAutoHeight() {
return;
}
const tabBottom = tabMainEl.getBoundingClientRect().bottom;
const contentBottom = contentEl.getBoundingClientRect().bottom;
const slackPx = Math.floor(tabBottom - contentBottom);
const nextCombinedHeight = Math.max(
DEFAULT_OVERVIEW_PLOT_HEIGHT_PX + DEFAULT_SPECTRUM_PLOT_HEIGHT_PX,
currentOverviewHeight + currentSpectrumHeight + slackPx - 2,
);
const nextHeight = Math.max(
DEFAULT_SPECTRUM_PLOT_HEIGHT_PX,
Math.round(nextCombinedHeight / 2),
const bounds = spectrumHeightBoundsPx(tabMainEl, contentEl, spectrumCanvasEl);
const requestedSpectrumHeight = spectrumManualPlotHeightPx == null
? bounds.max
: spectrumManualPlotHeightPx;
const nextSpectrumHeight = Math.max(
bounds.min,
Math.min(bounds.max, Math.round(requestedSpectrumHeight)),
);
if (spectrumManualPlotHeightPx != null) {
spectrumManualPlotHeightPx = nextSpectrumHeight;
}
if (
Math.abs(nextHeight - currentOverviewHeight) < 2
&& Math.abs(nextHeight - currentSpectrumHeight) < 2
Math.abs(DEFAULT_OVERVIEW_PLOT_HEIGHT_PX - currentOverviewHeight) < 2
&& Math.abs(nextSpectrumHeight - currentSpectrumHeight) < 2
) return;
root.style.setProperty("--overview-plot-height", `${nextHeight}px`);
root.style.setProperty("--spectrum-plot-height", `${nextHeight}px`);
root.style.setProperty("--overview-plot-height", `${DEFAULT_OVERVIEW_PLOT_HEIGHT_PX}px`);
root.style.setProperty("--spectrum-plot-height", `${nextSpectrumHeight}px`);
if (lastSpectrumData) {
scheduleSpectrumDraw();
scheduleOverviewDraw();
}
}
function beginSpectrumResize(clientY) {
const tabMainEl = document.getElementById("tab-main");
const contentEl = document.getElementById("content");
const spectrumCanvasEl = document.getElementById("spectrum-canvas");
const spectrumPanelEl = document.getElementById("spectrum-panel");
if (!tabMainEl || !contentEl || !spectrumCanvasEl || !spectrumPanelEl) return false;
if (getComputedStyle(spectrumPanelEl).display === "none") return false;
spectrumResizeState = {
startY: clientY,
startHeight: currentSpectrumHeightPx(spectrumCanvasEl),
};
document.body.classList.add("spectrum-resizing");
return true;
}
function updateSpectrumResize(clientY) {
if (!spectrumResizeState) return;
const deltaY = clientY - spectrumResizeState.startY;
spectrumManualPlotHeightPx = spectrumResizeState.startHeight + deltaY;
updateSpectrumAutoHeight();
}
function endSpectrumResize() {
spectrumResizeState = null;
document.body.classList.remove("spectrum-resizing");
}
const spectrumSizeGrip = document.getElementById("spectrum-size-grip");
if (spectrumSizeGrip) {
spectrumSizeGrip.addEventListener("pointerdown", (event) => {
if (event.button !== 0) return;
if (!beginSpectrumResize(event.clientY)) return;
event.preventDefault();
if (typeof spectrumSizeGrip.setPointerCapture === "function") {
spectrumSizeGrip.setPointerCapture(event.pointerId);
}
});
spectrumSizeGrip.addEventListener("pointermove", (event) => {
if (!spectrumResizeState) return;
updateSpectrumResize(event.clientY);
});
const finishResize = (event) => {
if (!spectrumResizeState) return;
if (typeof spectrumSizeGrip.releasePointerCapture === "function" && spectrumSizeGrip.hasPointerCapture(event.pointerId)) {
spectrumSizeGrip.releasePointerCapture(event.pointerId);
}
endSpectrumResize();
};
spectrumSizeGrip.addEventListener("pointerup", finishResize);
spectrumSizeGrip.addEventListener("pointercancel", finishResize);
spectrumSizeGrip.addEventListener("dblclick", () => {
spectrumManualPlotHeightPx = null;
scheduleSpectrumLayout();
});
}
function updateTitle() {
const titleEl = document.getElementById("rig-title");
if (titleEl) {
@@ -95,6 +95,7 @@
<div id="spectrum-tooltip"></div>
<div id="spectrum-freq-axis"></div>
</div>
<div id="spectrum-size-grip" title="Drag to resize spectrum height" aria-label="Resize spectrum height"></div>
<div id="spectrum-controls">
<div id="spectrum-bw-row">
<label id="spectrum-bw-label">Bandwidth <input type="number" id="spectrum-bw-input" value="" step="0.1" min="0.1" /> kHz</label>
@@ -2111,6 +2111,11 @@ button:focus-visible, input:focus-visible, select:focus-visible {
#spectrum-panel {
margin-bottom: 0;
}
.spectrum-resizing,
.spectrum-resizing * {
cursor: ns-resize !important;
user-select: none !important;
}
.spectrum-wrap {
position: relative;
width: 100%;
@@ -2184,6 +2189,26 @@ button:focus-visible, input:focus-visible, select:focus-visible {
border: 1px solid var(--border);
border-top: none;
}
#spectrum-size-grip {
position: relative;
height: 0.9rem;
margin-top: 0.1rem;
margin-bottom: 0.3rem;
cursor: ns-resize;
touch-action: none;
}
#spectrum-size-grip::before {
content: "";
position: absolute;
left: 50%;
top: 50%;
width: 3.8rem;
height: 0.26rem;
transform: translate(-50%, -50%);
border-radius: 999px;
border: 1px solid color-mix(in srgb, var(--border-light) 76%, transparent);
background: color-mix(in srgb, var(--btn-bg) 78%, transparent);
}
#spectrum-freq-axis span {
position: absolute;
transform: translateX(-50%);