From 513ab178ec737f1afda54a5bd7c85b2bbce7916c Mon Sep 17 00:00:00 2001 From: rouggy Date: Fri, 24 Jul 2026 11:12:32 +0200 Subject: [PATCH] feat: collapse advanced FlexRadio DSP behind a 'DSP' toggle The RECEIVE card had grown tall after adding WNB and the SmartSDR v4 DSP rows (NRL/NRS/NRF/ANFL/RNN/ANFT). Keep the everyday NB/NR/ANF visible and tuck the supplementary controls behind a collapsible 'DSP' button. The button badges + highlights when any hidden control is ON so an active filter is never hidden, and its open/closed state persists in localStorage (opslog.flexDspOpen). --- changelog.json | 2 + frontend/src/components/FlexPanel.tsx | 123 ++++++++++++++++---------- frontend/src/lib/i18n.tsx | 4 +- 3 files changed, 80 insertions(+), 49 deletions(-) diff --git a/changelog.json b/changelog.json index a7d60a3..00643cf 100644 --- a/changelog.json +++ b/changelog.json @@ -3,6 +3,7 @@ "version": "0.20.12", "date": "2026-07-23", "en": [ + "FlexRadio panel: the RECEIVE card is shorter again. All the noise controls added recently made it tower, so only the everyday ones — NB, NR, ANF — now stay visible; WNB and the SmartSDR v4 DSP block (NRL/NRS/NRF/ANFL and the AI/FFT RNN & ANFT) tuck behind a 'DSP' button you expand when you need them. The button carries a dot and highlights when one of the hidden controls is switched on, so nothing active is ever out of sight, and its open/closed state is remembered.", "ADIF export can now pick exactly which fields to write. The global 'Export to ADIF' dialog gains a third choice, 'Choose fields…', alongside 'Standard ADIF fields' and 'All OpsLog fields'; and right-clicking selected QSOs adds 'Export selected — choose fields…'. The picker separates the official ADIF 3.1.7 fields (grouped by category) from OpsLog / non-standard tags actually present in your log, with All/None per group and a one-click reset to defaults. Your selection is remembered for next time.", "Fixed 'Incorrect string value' (MySQL error 1366) when a QSO contained non-Latin characters — Cyrillic (Я), Polish ł, etc. — e.g. updating from QRZ. It happened when the shared MySQL database had been pre-created by the hosting panel as latin1, so OpsLog's tables couldn't store those letters. OpsLog now converts the database and its tables to utf8mb4 on connect (once, automatically), and everything stores correctly. Local SQLite logbooks were never affected.", "NET Control: the on-air list now follows a mic-pass order you control instead of log time. A pinned '#' column numbers the round, ⬆⬇ buttons move the selected station up/down the queue, new check-ins join the end, and 'Log & end' advances to the next in line. The order is remembered per net. Header sorting is disabled on this list so a click can't shuffle the round.", @@ -13,6 +14,7 @@ "Fixed the colour theme sometimes resetting to light after an update/relaunch: an update can clear the WebView's localStorage, and the fallback that restores the theme from the local settings database gave up after ~2.4s — occasionally too soon during the brief startup window before that store is ready. It now keeps retrying until the store actually answers, so a dark theme is reliably restored." ], "fr": [ + "Panneau FlexRadio : la carte RÉCEPTION est de nouveau plus compacte. Tous les contrôles de bruit ajoutés récemment la faisaient s'allonger, donc seuls ceux du quotidien — NB, NR, ANF — restent visibles ; le WNB et le bloc DSP SmartSDR v4 (NRL/NRS/NRF/ANFL ainsi que RNN & ANFT IA/FFT) se replient derrière un bouton « DSP » que tu déplies au besoin. Le bouton porte un point et s'illumine quand l'un des contrôles masqués est activé, pour ne jamais perdre de vue quelque chose d'actif, et son état ouvert/fermé est mémorisé.", "L'export ADIF permet maintenant de choisir précisément les champs à écrire. La fenêtre globale « Exporter en ADIF » gagne un troisième choix, « Choisir les champs… », à côté de « Champs ADIF standard » et « Tous les champs OpsLog » ; et le clic droit sur des QSO sélectionnés ajoute « Exporter la sélection — choisir les champs… ». Le sélecteur sépare les champs ADIF 3.1.7 officiels (regroupés par catégorie) des balises OpsLog / non standard réellement présentes dans ton log, avec Tout/Aucun par groupe et un retour aux valeurs par défaut en un clic. Ta sélection est mémorisée pour la prochaine fois.", "Correction de « Incorrect string value » (erreur MySQL 1366) quand un QSO contenait des caractères non latins — cyrillique (Я), polonais ł, etc. — p. ex. lors d'une mise à jour depuis QRZ. Ça arrivait quand la base MySQL partagée avait été pré-créée en latin1 par le panel d'hébergement, empêchant les tables d'OpsLog de stocker ces lettres. OpsLog convertit désormais la base et ses tables en utf8mb4 à la connexion (une seule fois, automatiquement), et tout s'enregistre correctement. Les logbooks SQLite locaux n'étaient pas concernés.", "NET Control : la liste on-air suit maintenant un ordre de passage du micro que tu contrôles, au lieu de l'heure de log. Une colonne « # » épinglée numérote le tour, les boutons ⬆⬇ montent/descendent la station sélectionnée dans la file, les nouveaux arrivants rejoignent la fin, et « Logger & terminer » passe au suivant. L'ordre est mémorisé par NET. Le tri par en-tête est désactivé sur cette liste pour qu'un clic ne bouscule pas le tour.", diff --git a/frontend/src/components/FlexPanel.tsx b/frontend/src/components/FlexPanel.tsx index 9b5404b..b946b95 100644 --- a/frontend/src/components/FlexPanel.tsx +++ b/frontend/src/components/FlexPanel.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef, useState } from 'react'; -import { Radio, Zap, Power, AudioLines, Flame, Gauge, Volume2, VolumeX } from 'lucide-react'; +import { Radio, Zap, Power, AudioLines, Flame, Gauge, Volume2, VolumeX, ChevronDown, SlidersHorizontal } from 'lucide-react'; import { GetFlexState, FlexSetPower, FlexSetTunePower, FlexTune, FlexSetVox, FlexSetVoxLevel, FlexSetVoxDelay, FlexSetProcessor, FlexSetProcessorLevel, FlexSetMon, FlexSetMonLevel, FlexSetMic, @@ -295,6 +295,11 @@ function powerLevelLabel(pl?: string): string { export function FlexPanel({ onCWSpeed, onReportRST }: { onCWSpeed?: (wpm: number) => void; onReportRST?: (rst: string) => void } = {}) { const { t } = useI18n(); const [st, setSt] = useState(ZERO); + // Extra/"advanced" DSP rows (WNB + the SmartSDR v4 NRL/NRS/NRF/ANFL/AI-FFT + // block) collapse behind a button so the RECEIVE card doesn't grow tall — only + // NB/NR/ANF stay visible by default. Remembered across sessions. + const [dspOpen, setDspOpen] = useState(() => localStorage.getItem('opslog.flexDspOpen') === '1'); + const toggleDsp = () => setDspOpen((o) => { const n = !o; localStorage.setItem('opslog.flexDspOpen', n ? '1' : '0'); return n; }); const hold = useRef>({}); // Keep the host's keyer speed (used for CW-length estimates and the keyer panel) // in step with the radio's ACTUAL CW speed — including when it's changed on the @@ -768,13 +773,28 @@ export function FlexPanel({ onCWSpeed, onReportRST }: { onCWSpeed?: (wpm: number {st.agc_threshold}
+ {/* Core noise controls stay visible; WNB + the SmartSDR v4 DSP block + hide behind the 'DSP' toggle so the card doesn't tower. When any + hidden control is ON we badge the toggle so it's not forgotten. */} + {(() => { + const hiddenOn = st.wnb || (st.dsp_v4 && (!!st.lms_nr || !!st.speex_nr || !!st.nrf || (!isCW && !!st.lms_anf) || !!st.rnn || (!isCW && !!st.anft))); + return ( +
+ {t('flxp.dspNoise')} + +
+ ); + })()} change('nb', !st.nb, () => FlexSetNB(!st.nb))} onLevel={(v) => change('nb_level', v, () => FlexSetNBLevel(v))} /> - {/* WNB — wideband noise blanker (the extra hardware NR the Flex has). */} - change('wnb', !st.wnb, () => FlexSetWNB(!st.wnb))} - onLevel={(v) => change('wnb_level', v, () => FlexSetWNBLevel(v))} /> change('nr', !st.nr, () => FlexSetNR(!st.nr))} onLevel={(v) => change('nr_level', v, () => FlexSetNRLevel(v))} /> @@ -784,50 +804,59 @@ export function FlexPanel({ onCWSpeed, onReportRST }: { onCWSpeed?: (wpm: number onToggle={() => change('anf', !st.anf, () => FlexSetANF(!st.anf))} onLevel={(v) => change('anf_level', v, () => FlexSetANFLevel(v))} /> )} -
- {/* SmartSDR v4 DSP (8000/Aurora series) — NRL/ANFL (legacy LMS), NRS - (spectral subtraction), NRF (NR w/ filter), RNN (AI NR), ANFT (FFT - notch). Only shown when the radio actually reports these slice keys - (older 6000s never do). API keys per the FlexLib slice docs. */} - {st.dsp_v4 && ( -
- change('lms_nr', !st.lms_nr, () => FlexSetLMSNR(!st.lms_nr))} - onLevel={(v) => change('lms_nr_level', v, () => FlexSetLMSNRLevel(v))} /> - change('speex_nr', !st.speex_nr, () => FlexSetSpeexNR(!st.speex_nr))} - onLevel={(v) => change('speex_nr_level', v, () => FlexSetSpeexNRLevel(v))} /> - change('nrf', !st.nrf, () => FlexSetNRF(!st.nrf))} - onLevel={(v) => change('nrf_level', v, () => FlexSetNRFLevel(v))} /> - {/* Notch filters target carriers in voice — hidden in CW like ANF. */} - {!isCW && ( - change('lms_anf', !st.lms_anf, () => FlexSetLMSANF(!st.lms_anf))} - onLevel={(v) => change('lms_anf_level', v, () => FlexSetLMSANFLevel(v))} /> - )} - {/* RNN and ANFT are on/off only — no level in the API. */} -
- AI/FFT - - {!isCW && ( - + {/* Advanced / supplementary DSP — collapsed by default. WNB (wideband + noise blanker) plus the SmartSDR v4 block (8000/Aurora): NRL/ANFL + (legacy LMS), NRS (spectral subtraction), NRF (NR w/ filter), RNN + (AI NR), ANFT (FFT notch). The v4 rows only exist when the radio + reports those slice keys (older 6000s never do). */} + {dspOpen && ( +
+ {/* WNB — wideband noise blanker (the extra hardware NR the Flex has). */} + change('wnb', !st.wnb, () => FlexSetWNB(!st.wnb))} + onLevel={(v) => change('wnb_level', v, () => FlexSetWNBLevel(v))} /> + {st.dsp_v4 && ( + <> + change('lms_nr', !st.lms_nr, () => FlexSetLMSNR(!st.lms_nr))} + onLevel={(v) => change('lms_nr_level', v, () => FlexSetLMSNRLevel(v))} /> + change('speex_nr', !st.speex_nr, () => FlexSetSpeexNR(!st.speex_nr))} + onLevel={(v) => change('speex_nr_level', v, () => FlexSetSpeexNRLevel(v))} /> + change('nrf', !st.nrf, () => FlexSetNRF(!st.nrf))} + onLevel={(v) => change('nrf_level', v, () => FlexSetNRFLevel(v))} /> + {/* Notch filters target carriers in voice — hidden in CW like ANF. */} + {!isCW && ( + change('lms_anf', !st.lms_anf, () => FlexSetLMSANF(!st.lms_anf))} + onLevel={(v) => change('lms_anf_level', v, () => FlexSetLMSANFLevel(v))} /> + )} + {/* RNN and ANFT are on/off only — no level in the API. */} +
+ AI/FFT + + {!isCW && ( + + )} +
+ )}
-
- )} + )} +
{isCW && (