diff --git a/app.go b/app.go
index e314c61..c560a2c 100644
--- a/app.go
+++ b/app.go
@@ -1851,6 +1851,8 @@ func (a *App) AddQSO(q qso.QSO) (id int64, err error) {
}
if err == nil {
q.ID = id
+ // Announce the log so UI widgets can react (e.g. the Flex panel zeroing RIT).
+ wruntime.EventsEmit(a.ctx, "qso:logged", id)
a.saveQSORecording(&q)
if a.extsvc != nil {
a.extsvc.OnQSOLogged(id)
diff --git a/frontend/src/components/FlexPanel.tsx b/frontend/src/components/FlexPanel.tsx
index aee0b2d..57cc16e 100644
--- a/frontend/src/components/FlexPanel.tsx
+++ b/frontend/src/components/FlexPanel.tsx
@@ -12,6 +12,7 @@ import {
FlexSetAPF, FlexSetAPFLevel, FlexSetCWSpeed, FlexSetCWPitch, FlexSetCWBreakInDelay,
FlexSetCWSidetone, FlexSetSidetoneLevel, FlexSetCWFilter, FlexSetFilter,
} from '../../wailsjs/go/main/App';
+import { EventsOn } from '../../wailsjs/runtime/runtime';
import { cn } from '@/lib/utils';
import { useI18n } from '@/lib/i18n';
import { sMeterRST } from '@/lib/rst';
@@ -189,7 +190,7 @@ function OffsetRow({ label, on, onToggle, hz, onHz, disabled, title }: {
return (
-
@@ -317,6 +318,36 @@ export function FlexPanel({ onCWSpeed, onReportRST }: { onCWSpeed?: (wpm: number
const off = !st.available;
const rxOff = off || !st.rx_avail;
+
+ // Radio-style global RIT tuning: Ctrl+←/→ nudges the RIT offset (Ctrl+Shift =
+ // ±100 Hz) from anywhere, without having to click the RIT field first. A
+ // focused offset row (data-offsetrow) or a text field handles its own keys, so
+ // this never double-fires. Only active while RIT is on and the RX is available.
+ useEffect(() => {
+ const onKey = (e: KeyboardEvent) => {
+ if (!(e.ctrlKey || e.metaKey)) return;
+ if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') return;
+ const ae = document.activeElement as HTMLElement | null;
+ if (ae && (ae.hasAttribute('data-offsetrow') || /^(input|textarea|select)$/i.test(ae.tagName) || ae.isContentEditable)) return;
+ if (!st.rit || rxOff) return;
+ e.preventDefault();
+ const mag = e.shiftKey ? 100 : 10;
+ const next = (st.rit_freq || 0) + (e.key === 'ArrowRight' ? mag : -mag);
+ change('rit_freq', next, () => FlexSetRITFreq(next));
+ };
+ window.addEventListener('keydown', onKey);
+ return () => window.removeEventListener('keydown', onKey);
+ }, [st.rit, st.rit_freq, rxOff]);
+
+ // Clear the RIT offset back to 0 whenever a QSO is logged (any path: Log
+ // button, keyer macro, WSJT-X), so the next station starts centred. Only acts
+ // when there's actually a non-zero offset to clear.
+ useEffect(() => {
+ const off = EventsOn('qso:logged', () => {
+ if (st.rit && (st.rit_freq || 0) !== 0) change('rit_freq', 0, () => FlexSetRITFreq(0));
+ });
+ return () => off();
+ }, [st.rit, st.rit_freq]);
const isCW = (st.mode || '').toUpperCase().includes('CW');
const PROC = [{ v: '0', l: 'NOR' }, { v: '1', l: 'DX' }, { v: '2', l: 'DX+' }];
const AGC = [{ v: 'off', l: 'OFF' }, { v: 'slow', l: 'SLOW' }, { v: 'med', l: 'MED' }, { v: 'fast', l: 'FAST' }];