feat: scroll-tune the top frequency readout (100/10/1 kHz per digit)
Rolling the mouse wheel over the hundreds / tens / units-of-kHz digit of the header (and compact top-bar) frequency steps the frequency by 100 / 10 / 1 kHz (wheel up = higher). The display updates optimistically on every notch and the rig QSYs over CAT, debounced so a fast scroll doesn't flood the link. Only the kHz digits are wheel-sensitive; MHz and Hz stay static. New FreqWheelDisplay component (replaces the plain fmtFreqDots span in both the full header and the compact top bar) + nudgeFreqHz handler.
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
"version": "0.21.1",
|
||||
"date": "2026-07-24",
|
||||
"en": [
|
||||
"The top frequency readout is now scroll-tunable: roll the mouse wheel over the hundreds / tens / units-of-kHz digit to step the frequency by 100 / 10 / 1 kHz, and the rig follows over CAT.",
|
||||
"New: 4O3A Tuner Genius XL control. Enable it in Settings → Tuner Genius (IP only; port is fixed at 9010). Live SWR and forward power with Tune, Bypass and Operate/Standby, the two channels A/B (source, frequency and antenna) shown and click-selectable. Available as a docked widget, a card in the FlexRadio panel (like the PowerGenius) and a card in Station Control. Controlled directly over TCP, so it uses just one of the box's connection slots.",
|
||||
"FlexRadio panel tidy-up: cards collapse from the chevron in their header (Transmit and Receive fold together), all meters are the same size, the MIC and COMP meters only show in phone modes, and the S-meter dBm now sits next to the S-value instead of on a second line.",
|
||||
"Fixed the colour theme sometimes reverting to the default when reopening OpsLog — it's now restored reliably.",
|
||||
@@ -13,6 +14,7 @@
|
||||
"Fixed award references (in the entry strip's Awards tab) not clearing when the callsign changes — clicking one spot then another no longer keeps the previous station's references."
|
||||
],
|
||||
"fr": [
|
||||
"L'affichage de fréquence en haut est maintenant accordable à la molette : roule la molette sur le chiffre des centaines / dizaines / unités de kHz pour changer la fréquence par pas de 100 / 10 / 1 kHz, et la radio suit en CAT.",
|
||||
"Nouveau : contrôle du 4O3A Tuner Genius XL. Active-le dans Réglages → Tuner Genius (IP seulement ; port fixé à 9010). ROS et puissance directe en direct avec Accord, Bypass et Operate/Standby, et les deux canaux A/B (source, fréquence et antenne) affichés et sélectionnables d'un clic. Disponible en widget ancré, en carte dans le panneau FlexRadio (comme le PowerGenius) et en carte dans Station Control. Piloté directement en TCP, il n'utilise qu'une des connexions de la boîte.",
|
||||
"Nettoyage du panneau FlexRadio : les cartes se replient via le chevron de leur en-tête (Transmit et Receive se replient ensemble), tous les meters ont la même taille, les meters MIC et COMP ne s'affichent qu'en phonie, et le dBm du S-mètre est maintenant à côté de la valeur S plutôt que sur une deuxième ligne.",
|
||||
"Correction du thème qui revenait parfois au défaut à la réouverture d'OpsLog — il est maintenant restauré de façon fiable.",
|
||||
|
||||
+56
-2
@@ -211,6 +211,36 @@ function fmtFreqDots(mhzStr: string): string {
|
||||
const frac = (fracRaw + '000000').slice(0, 6);
|
||||
return `${intPart}.${frac.slice(0, 3)}.${frac.slice(3, 6)}`;
|
||||
}
|
||||
|
||||
// FreqWheelDisplay renders a frequency (MHz string) like fmtFreqDots — MHz.kHz.Hz
|
||||
// — but makes the three kHz digits scroll-sensitive: rolling the mouse wheel over
|
||||
// the hundreds / tens / units-of-kHz digit steps the frequency by 100 / 10 / 1 kHz
|
||||
// (up = wheel up). onNudge receives the delta in Hz. The MHz and Hz digits are
|
||||
// static (only kHz stepping was requested). Used in the header + compact top bar.
|
||||
function FreqWheelDisplay({ mhz, onNudge, className, placeholder = '—.———.———' }: {
|
||||
mhz: string; onNudge: (deltaHz: number) => void; className?: string; placeholder?: string;
|
||||
}) {
|
||||
if (!mhz) return <span className={className}>{placeholder}</span>;
|
||||
const [intPart, fracRaw = ''] = mhz.split('.');
|
||||
const frac = (fracRaw + '000000').slice(0, 6);
|
||||
const khz = frac.slice(0, 3); // [hundreds, tens, units] of kHz
|
||||
const hz = frac.slice(3, 6);
|
||||
const stepHz = [100_000, 10_000, 1_000]; // per kHz digit: 100 / 10 / 1 kHz
|
||||
return (
|
||||
<span className={className}>
|
||||
{intPart}.
|
||||
{khz.split('').map((d, i) => (
|
||||
<span key={i}
|
||||
onWheel={(e) => { e.preventDefault(); e.stopPropagation(); onNudge(e.deltaY < 0 ? stepHz[i] : -stepHz[i]); }}
|
||||
className="cursor-ns-resize rounded-[2px] hover:bg-primary/25 transition-colors"
|
||||
title="Scroll to change frequency">
|
||||
{d}
|
||||
</span>
|
||||
))}
|
||||
.{hz}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
// shortCatError condenses a backend error into a few words for the topbar
|
||||
// pill. The full message stays in the tooltip. Recognises the common cases
|
||||
// (OmniRig not installed, not registered) and otherwise truncates.
|
||||
@@ -3543,6 +3573,30 @@ export default function App() {
|
||||
noteManualEdit();
|
||||
SetCATFrequency(Math.round(mhz * 1_000_000)).catch(() => {});
|
||||
};
|
||||
// Mouse-wheel over a kHz digit of the top frequency readout: step the frequency
|
||||
// and (if CAT is connected) QSY the rig. The display updates optimistically on
|
||||
// every notch; the actual radio tune is debounced so a fast scroll doesn't flood
|
||||
// the CAT link. nudgeAccumRef holds the live value across a burst (setFreqMhz is
|
||||
// async, so we can't re-read it between notches).
|
||||
const nudgeAccumRef = useRef<number | null>(null);
|
||||
const nudgeCatTimer = useRef<number | null>(null);
|
||||
const nudgeFreqHz = (deltaHz: number) => {
|
||||
let cur = nudgeAccumRef.current;
|
||||
if (cur == null) cur = freqMhz ? Math.round(parseFloat(freqMhz) * 1_000_000) : 0;
|
||||
if (!cur) return;
|
||||
const newHz = Math.max(0, cur + deltaHz);
|
||||
nudgeAccumRef.current = newHz;
|
||||
setFreqMhz((newHz / 1_000_000).toFixed(6));
|
||||
noteManualEdit();
|
||||
const b = bandForMHz(newHz / 1_000_000); if (b) setBand(b);
|
||||
if (nudgeCatTimer.current) window.clearTimeout(nudgeCatTimer.current);
|
||||
nudgeCatTimer.current = window.setTimeout(() => {
|
||||
const hz = nudgeAccumRef.current;
|
||||
nudgeAccumRef.current = null;
|
||||
nudgeCatTimer.current = null;
|
||||
if (hz && catState.enabled && catState.connected) SetCATFrequency(hz).catch(() => {});
|
||||
}, 150);
|
||||
};
|
||||
const freqBlock = (
|
||||
<div className="flex flex-col w-32">
|
||||
<Label className="mb-1 h-3.5 flex items-center gap-1">{t('field.txFreq')} <LockBtn k="freq" title="frequency" /></Label>
|
||||
@@ -3936,7 +3990,7 @@ export default function App() {
|
||||
<span className="font-bold text-xs tracking-tight">OpsLog</span>
|
||||
</div>
|
||||
<div className="flex items-baseline gap-1.5 font-mono ml-2">
|
||||
<span className="text-sm font-semibold text-primary">{freqMhz ? fmtFreqDots(freqMhz) : '—.———.———'}</span>
|
||||
<FreqWheelDisplay mhz={freqMhz} onNudge={nudgeFreqHz} className="text-sm font-semibold text-primary" />
|
||||
<span className="text-[9px] text-muted-foreground">MHz</span>
|
||||
<Badge variant="accent" className="font-mono ml-2 text-[9px] py-0">{band}</Badge>
|
||||
<Badge className="bg-success-muted text-success-muted-foreground hover:bg-success-muted font-mono text-[9px] py-0" variant="outline">{mode}</Badge>
|
||||
@@ -3966,7 +4020,7 @@ export default function App() {
|
||||
{/* Toasts and errors live in the STATUS BAR at the bottom now — the
|
||||
header band was too narrow and long messages were cut off. */}
|
||||
<div className="flex flex-col items-end leading-none">
|
||||
<span className="text-2xl font-semibold text-primary tracking-wide">{freqMhz ? fmtFreqDots(freqMhz) : '—.———.———'}</span>
|
||||
<FreqWheelDisplay mhz={freqMhz} onNudge={nudgeFreqHz} className="text-2xl font-semibold text-primary tracking-wide" />
|
||||
{catState.split && rxFreqMhz && (
|
||||
<span className="text-[10px] text-muted-foreground mt-0.5">
|
||||
<span className="text-danger font-semibold mr-1">RX</span>
|
||||
|
||||
Reference in New Issue
Block a user