From a905fa96789cbdbc89a0b7fb18aee761175a1a05 Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Thu, 5 Mar 2026 17:45:50 +0100 Subject: [PATCH] [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 Signed-off-by: Stan Grams --- .../trx-frontend-http/assets/web/app.js | 109 +++++++++++++++--- .../trx-frontend-http/assets/web/index.html | 1 + .../trx-frontend-http/assets/web/style.css | 25 ++++ 3 files changed, 121 insertions(+), 14 deletions(-) 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 7e8b7ae..6611a92 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 @@ -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) { 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 ff19366..65e6537 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 @@ -95,6 +95,7 @@
+
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css index 18c10bd..3157066 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css @@ -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%);