From e29b7ed3d386231066da1cb053334c49974898c2 Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Mon, 9 Mar 2026 23:10:18 +0100 Subject: [PATCH] [fix](trx-frontend-http): reduce INP on freq input from ~1500ms to <50ms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chrome classifies wheel events as "pointer" interactions and tracks async continuations initiated by the handler. The wheel-on-freq-input path was: jogFreq → setRigFrequency → postPath(/set_freq) [~700ms] → ensureTunedBandwidthCoverage [~700ms] ...two sequential network round-trips totalling ~1400ms of INP. Three changes: 1. yieldToMain(): add a scheduler.yield() / setTimeout(0) helper that yields the main thread back to the browser. Chrome's INP interaction tracking ends at the yield point, so the network RTT no longer counts. 2. jogFreq: call applyLocalTunedFrequency() optimistically before the yield so the freq display updates are visible in the very next paint, then yield before firing any network requests. 3. setRigFrequency: move applyLocalTunedFrequency() before the awaits (consistent optimistic-update contract for all callers), and run postPath(/set_freq) and ensureTunedBandwidthCoverage() in parallel via Promise.all — they are independent server operations. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Stan Grams --- .../trx-frontend-http/assets/web/app.js | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 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 11a2cbf..73cf5ab 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 @@ -1457,9 +1457,13 @@ async function setRigFrequency(freqHz) { showUnsupportedFreqPopup(targetHz); throw new Error(`Unsupported frequency: ${targetHz}`); } - await postPath(`/set_freq?hz=${targetHz}`); + // Optimistic local update before any network round-trip. applyLocalTunedFrequency(targetHz); - await ensureTunedBandwidthCoverage(targetHz); + // set_freq and set_center_freq are independent server operations — run in parallel. + await Promise.all([ + postPath(`/set_freq?hz=${targetHz}`), + ensureTunedBandwidthCoverage(targetHz), + ]); } function spectrumBinIndexForHz(data, hz) { @@ -2750,6 +2754,15 @@ function disconnect() { } } +// Yield the main thread so the browser can paint before heavy async work. +// Uses scheduler.yield() (Chrome 115+) with a setTimeout fallback. +function yieldToMain() { + if (typeof scheduler !== "undefined" && typeof scheduler.yield === "function") { + return scheduler.yield(); + } + return new Promise((resolve) => setTimeout(resolve, 0)); +} + async function postPath(path) { const resp = await fetch(path, { method: "POST" }); if (authEnabled && resp.status === 401) { @@ -2949,6 +2962,11 @@ async function jogFreq(direction) { jogAngle = (jogAngle + direction * 15) % 360; jogIndicator.style.transform = `translateX(-50%) rotate(${jogAngle}deg)`; showHint("Setting frequency…"); + // Optimistic local state update — visible to the user before the network call. + applyLocalTunedFrequency(newHz); + // Yield so the browser paints the updated freq display before the network + // round-trip begins. Chrome's INP tracking ends at the yield point. + await yieldToMain(); try { await setRigFrequency(newHz); showHint("Freq set", 1000);