feat(update): Later asks how much later

"Later" set updateInfo to null and nothing else, while the check behind
it runs on a five-minute interval — so the notice came back four times
an hour, all evening, for a version the operator had already declined.
The button was doing exactly what it said and was useless anyway.

It now offers 1, 4, 12 or 24 hours, and the deferral is recorded with
the VERSION it applies to. That scoping is the part that matters: a
release newer than the one put off is different news and appears at
once, so a snooze can defer an update but never bury one.

The cross hides it for an hour — the shortest of the four — rather than
for the five minutes until the next poll, which is what made the notice
feel broken. The background check honours the deferral; checkUpdateNow,
behind About, deliberately does not, because asking is asking.

Kept out of the portable UI prefs: a machine told to wait four hours has
said nothing about the operator's other machines.
This commit is contained in:
2026-09-10 13:43:06 +02:00
parent f998023a28
commit 8359caab16
3 changed files with 57 additions and 10 deletions
+6 -2
View File
@@ -12,7 +12,9 @@
"Tracking now shows where the satellite is beside where the antenna points: azimuth, elevation, distance and altitude, in the same strip as the two frequencies. The elevation goes dim below the horizon, so a bird still being followed on its way up cannot be read as workable.", "Tracking now shows where the satellite is beside where the antenna points: azimuth, elevation, distance and altitude, in the same strip as the two frequencies. The elevation goes dim below the horizon, so a bird still being followed on its way up cannot be read as workable.",
"The two satellite lists in the settings are sorted by name, numerically — AO-27, AO-91, AO-123. The left one came in the order the frequency file happens to be written and the right one in the order they were clicked, so finding one bird among sixteen meant reading all sixteen. A satellite renamed on getting its OSCAR number, as LILACSAT-2 became LO-90, is also recognised in a followed list that still holds the old name instead of being listed as having no elements.", "The two satellite lists in the settings are sorted by name, numerically — AO-27, AO-91, AO-123. The left one came in the order the frequency file happens to be written and the right one in the order they were clicked, so finding one bird among sixteen meant reading all sixteen. A satellite renamed on getting its OSCAR number, as LILACSAT-2 became LO-90, is also recognised in a followed list that still holds the old name instead of being listed as having no elements.",
"The grid-square map filters by one mode, by band and by satellite. The FTx button is gone: it grouped FT8, FT4 and FT2 together, while the question the map answers is where a single mode has been heard. The named modes are read from the log itself, so each one is offered only if there is something behind it — and FT2 appears for whoever is already using it, with nothing to change here the day it becomes a registered mode. The band list is the station's own plus anything worked outside it, and the satellite list only appears once a square has been worked through a bird.", "The grid-square map filters by one mode, by band and by satellite. The FTx button is gone: it grouped FT8, FT4 and FT2 together, while the question the map answers is where a single mode has been heard. The named modes are read from the log itself, so each one is offered only if there is something behind it — and FT2 appears for whoever is already using it, with nothing to change here the day it becomes a registered mode. The band list is the station's own plus anything worked outside it, and the satellite list only appears once a square has been worked through a bird.",
"Saving the satellite settings now refreshes the Satellites tab. It read the followed list once when it was opened, so un-following a satellite left it in the pass table and in the dropdown until the tab was reopened — which looks exactly like a setting that failed to save. The lowest pass, the window and the locator are picked up the same way, and a satellite that is no longer followed hands the selection to the first one that is." "Saving the satellite settings now refreshes the Satellites tab. It read the followed list once when it was opened, so un-following a satellite left it in the pass table and in the dropdown until the tab was reopened — which looks exactly like a setting that failed to save. The lowest pass, the window and the locator are picked up the same way, and a satellite that is no longer followed hands the selection to the first one that is.",
"The FT map takes one colour for every decode, next to the basemap buttons. The band palette is calibrated against a plain map — 60m navy and 70cm olive all but vanish over the satellite imagery — and it is only useful to somebody watching several bands at once, which most operators are not. Leave it empty for the colour per band, as before. The legend then keeps the band names and drops the swatches, since which bands are up is still worth knowing.",
"\"Later\" on an update notice now asks for how long: 1, 4, 12 or 24 hours. It only hid the card before, and the check behind it runs every five minutes, so the same notice came back four times an hour all evening for a version already declined. The delay is recorded against that version, so a newer release is different news and appears at once — a deferral can never bury an update. The cross hides it for an hour, and opening About still answers whatever was deferred, because asking is asking."
], ],
"fr": [ "fr": [
"SteppIR : un bouton Calibrer, à côté de Rétracter. Il amène chaque élément en butée pour que le contrôleur retrouve son zéro — le remède à une antenne qui saccorde à la mauvaise longueur après une coupure en pleine course, après avoir poussé les éléments à la main, ou après un moteur qui a glissé. Cela prend plusieurs minutes et lantenne est inutilisable jusqu’à la fin : une confirmation est donc demandée. Les contrôleurs Ultrabeam nont pas cette commande et le disent, au lieu de faire semblant. Rétracter les éléments existait déjà et sexplique maintenant : cest la position de rangement, et le prochain accord les fait ressortir tout seuls.", "SteppIR : un bouton Calibrer, à côté de Rétracter. Il amène chaque élément en butée pour que le contrôleur retrouve son zéro — le remède à une antenne qui saccorde à la mauvaise longueur après une coupure en pleine course, après avoir poussé les éléments à la main, ou après un moteur qui a glissé. Cela prend plusieurs minutes et lantenne est inutilisable jusqu’à la fin : une confirmation est donc demandée. Les contrôleurs Ultrabeam nont pas cette commande et le disent, au lieu de faire semblant. Rétracter les éléments existait déjà et sexplique maintenant : cest la position de rangement, et le prochain accord les fait ressortir tout seuls.",
@@ -24,7 +26,9 @@
"Le suivi affiche désormais où est le satellite à côté de là où pointe lantenne : azimut, élévation, distance et altitude, dans le même bandeau que les deux fréquences. L’élévation sestompe sous lhorizon, pour quun satellite encore suivi avant son lever ne soit pas pris pour un satellite travaillable.", "Le suivi affiche désormais où est le satellite à côté de là où pointe lantenne : azimut, élévation, distance et altitude, dans le même bandeau que les deux fréquences. L’élévation sestompe sous lhorizon, pour quun satellite encore suivi avant son lever ne soit pas pris pour un satellite travaillable.",
"Les deux listes de satellites des réglages sont triées par nom, en tenant compte des nombres — AO-27, AO-91, AO-123. Celle de gauche suivait lordre du fichier de fréquences et celle de droite lordre des clics : retrouver un satellite parmi seize obligeait à lire les seize. Un satellite renommé lors de lattribution de son numéro OSCAR, comme LILACSAT-2 devenu LO-90, est aussi reconnu dans une liste de suivis qui porte encore lancien nom, au lieu dy apparaître sans éléments.", "Les deux listes de satellites des réglages sont triées par nom, en tenant compte des nombres — AO-27, AO-91, AO-123. Celle de gauche suivait lordre du fichier de fréquences et celle de droite lordre des clics : retrouver un satellite parmi seize obligeait à lire les seize. Un satellite renommé lors de lattribution de son numéro OSCAR, comme LILACSAT-2 devenu LO-90, est aussi reconnu dans une liste de suivis qui porte encore lancien nom, au lieu dy apparaître sans éléments.",
"La carte des carrés locator se filtre par mode précis, par bande et par satellite. Le bouton FTx disparaît : il regroupait FT8, FT4 et FT2, alors que la question à laquelle répond cette carte est de savoir où un seul mode a été entendu. Les modes proposés sont lus dans le journal, donc chacun napparaît que sil y a quelque chose derrière — et FT2 est proposé à qui lutilise déjà, sans rien à changer ici le jour où il deviendra un mode officiel. La liste des bandes est celle de la station, complétée par ce qui a été travaillé en dehors, et la liste des satellites napparaît quune fois un carré travaillé par satellite.", "La carte des carrés locator se filtre par mode précis, par bande et par satellite. Le bouton FTx disparaît : il regroupait FT8, FT4 et FT2, alors que la question à laquelle répond cette carte est de savoir où un seul mode a été entendu. Les modes proposés sont lus dans le journal, donc chacun napparaît que sil y a quelque chose derrière — et FT2 est proposé à qui lutilise déjà, sans rien à changer ici le jour où il deviendra un mode officiel. La liste des bandes est celle de la station, complétée par ce qui a été travaillé en dehors, et la liste des satellites napparaît quune fois un carré travaillé par satellite.",
"Enregistrer les réglages satellite rafraîchit désormais longlet Satellites. Il lisait la liste des suivis une seule fois à son ouverture : retirer un satellite le laissait dans le tableau des passes et dans la liste déroulante jusqu’à la réouverture de longlet — ce qui ressemble exactement à un réglage qui ne sest pas enregistré. L’élévation minimale, la fenêtre de prévision et le locator sont pris en compte de la même façon, et un satellite qui nest plus suivi passe la sélection au premier qui lest." "Enregistrer les réglages satellite rafraîchit désormais longlet Satellites. Il lisait la liste des suivis une seule fois à son ouverture : retirer un satellite le laissait dans le tableau des passes et dans la liste déroulante jusqu’à la réouverture de longlet — ce qui ressemble exactement à un réglage qui ne sest pas enregistré. L’élévation minimale, la fenêtre de prévision et le locator sont pris en compte de la même façon, et un satellite qui nest plus suivi passe la sélection au premier qui lest.",
"La carte FT accepte une couleur unique pour tous les décodages, à côté des boutons de fond de carte. La palette par bande est calibrée sur une carte claire — le bleu marine du 60 m et lolive du 70 cm disparaissent presque sur limagerie satellite — et elle ne sert qu’à qui surveille plusieurs bandes à la fois, ce qui nest pas le cas de la plupart. Laissez-la vide pour retrouver la couleur par bande. La légende conserve alors les noms de bandes et abandonne les pastilles, car savoir quelles bandes sont ouvertes reste utile.",
"« Plus tard » sur une notification de mise à jour demande désormais combien de temps : 1, 4, 12 ou 24 heures. Le bouton ne faisait que masquer la fenêtre, et la vérification derrière tourne toutes les cinq minutes : la même notification revenait quatre fois par heure toute la soirée pour une version déjà refusée. Le report est enregistré pour cette version précise, donc une version plus récente est une autre nouvelle et saffiche immédiatement — un report ne peut jamais enterrer une mise à jour. La croix masque pendant une heure, et ouvrir « À propos » répond toujours, même sur une version reportée : demander, cest demander."
] ]
}, },
{ {
+47 -4
View File
@@ -2455,6 +2455,33 @@ export default function App() {
if (wlNoticeTimer.current) window.clearTimeout(wlNoticeTimer.current); if (wlNoticeTimer.current) window.clearTimeout(wlNoticeTimer.current);
wlNoticeTimer.current = window.setTimeout(() => setWlNotice(null), 4000); wlNoticeTimer.current = window.setTimeout(() => setWlNotice(null), 4000);
}), []); }), []);
// An update deferred stays deferred.
//
// "Later" only hid the card, and the check behind it runs every five
// minutes — so the same notice came back four times an hour, all evening,
// for a version already declined. It now records until WHEN, and for which
// version: a release newer than the one put off is a different piece of
// news and appears at once, so a snooze can never bury an update for good.
//
// Deliberately not a portable UI pref — a machine told to wait four hours
// has said nothing about the operator's other machines.
const SNOOZE_KEY = 'opslog.updateSnooze';
const updateSnoozed = (version: string) => {
try {
const raw = localStorage.getItem(SNOOZE_KEY);
if (!raw) return false;
const s = JSON.parse(raw) as { v?: string; until?: number };
return s?.v === version && Number(s?.until) > Date.now();
} catch { return false; } // unreadable is not snoozed
};
const snoozeUpdate = (hours: number) => {
try {
if (updateInfo) localStorage.setItem(SNOOZE_KEY, JSON.stringify({ v: updateInfo.latest, until: Date.now() + hours * 3600_000 }));
} catch { /* quota: the card just comes back, which is the old behaviour */ }
setLaterOpen(false);
setUpdateInfo(null);
};
const [laterOpen, setLaterOpen] = useState(false);
const [checkingUpdate, setCheckingUpdate] = useState(false); const [checkingUpdate, setCheckingUpdate] = useState(false);
// Fresh update check on demand (opening About), so it never shows a stale // Fresh update check on demand (opening About), so it never shows a stale
// "you're up to date". Clears updateInfo when the latest check finds nothing. // "you're up to date". Clears updateInfo when the latest check finds nothing.
@@ -2472,7 +2499,11 @@ export default function App() {
useEffect(() => { useEffect(() => {
if (localStorage.getItem('opslog.checkUpdates') === '0') return; if (localStorage.getItem('opslog.checkUpdates') === '0') return;
const check = () => CheckForUpdate().then((u: any) => { const check = () => CheckForUpdate().then((u: any) => {
if (u?.available && u?.latest) setUpdateInfo({ latest: String(u.latest), url: String(u.url ?? ''), downloadUrl: String(u.download_url ?? '') }); // The snooze is checked HERE and not in checkUpdateNow: opening About
// is a question, and it deserves the answer whatever was deferred.
if (u?.available && u?.latest && !updateSnoozed(String(u.latest))) {
setUpdateInfo({ latest: String(u.latest), url: String(u.url ?? ''), downloadUrl: String(u.download_url ?? '') });
}
}).catch(() => {}); }).catch(() => {});
check(); check();
const id = window.setInterval(check, 5 * 60 * 1000); const id = window.setInterval(check, 5 * 60 * 1000);
@@ -7422,16 +7453,28 @@ export default function App() {
</div> </div>
</div> </div>
) : ( ) : (
<div className="mt-2 flex items-center gap-2"> <div className="mt-2 flex items-center gap-2 flex-wrap">
<button onClick={startUpdate} className="h-7 px-3 rounded-md bg-primary text-primary-foreground text-xs font-medium hover:opacity-90"> <button onClick={startUpdate} className="h-7 px-3 rounded-md bg-primary text-primary-foreground text-xs font-medium hover:opacity-90">
{updateInfo.downloadUrl ? t('upd.install') : t('upd.download')} {updateInfo.downloadUrl ? t('upd.install') : t('upd.download')}
</button> </button>
<button onClick={() => setUpdateInfo(null)} className="h-7 px-2 text-xs text-muted-foreground hover:text-foreground">{t('upd.later')}</button> {laterOpen ? (
<span className="flex items-center gap-1 text-[11px] text-muted-foreground">
{t('upd.remindIn')}
{[1, 4, 12, 24].map((h) => (
<button key={h} onClick={() => snoozeUpdate(h)}
className="h-6 px-1.5 rounded border border-border text-[11px] tabular-nums hover:bg-muted text-foreground">
{h}h
</button>
))}
</span>
) : (
<button onClick={() => setLaterOpen(true)} className="h-7 px-2 text-xs text-muted-foreground hover:text-foreground">{t('upd.later')}</button>
)}
</div> </div>
)} )}
</div> </div>
{!updating && ( {!updating && (
<button onClick={() => setUpdateInfo(null)} className="text-muted-foreground hover:text-foreground shrink-0" title="Dismiss"> <button onClick={() => snoozeUpdate(1)} className="text-muted-foreground hover:text-foreground shrink-0" title={t('upd.dismissHour')}>
<X className="size-4" /> <X className="size-4" />
</button> </button>
)} )}
+4 -4
View File
@@ -19,7 +19,7 @@ const en: Dict = {
'live.offlineTip': 'Offline — no QSO logged in the last 5 minutes', 'live.offlineTip': 'Offline — no QSO logged in the last 5 minutes',
'live.stationsTitle': 'Stations on air', 'live.stationsEmpty': 'No station reporting yet.', 'live.stationsHide': 'Hide', 'live.stationsTitle': 'Stations on air', 'live.stationsEmpty': 'No station reporting yet.', 'live.stationsHide': 'Hide',
'upd.available': 'OpsLog v{v} available', 'upd.current': "You're on v{v}.", 'upd.available': 'OpsLog v{v} available', 'upd.current': "You're on v{v}.",
'upd.install': 'Update now', 'upd.download': 'Download', 'upd.later': 'Later', 'upd.install': 'Update now', 'upd.download': 'Download', 'upd.later': 'Later', 'upd.remindIn': 'Remind me in', 'upd.dismissHour': 'Hide this for an hour',
'upd.downloading': 'Downloading…', 'upd.installing': 'Installing…', 'upd.downloading': 'Downloading…', 'upd.installing': 'Installing…',
'upd.restartNote': 'OpsLog will restart on the new version.', 'upd.restartNote': 'OpsLog will restart on the new version.',
'upd.retry': 'Retry', 'upd.browser': 'Open page', 'upd.checking': 'Checking for updates…', 'upd.upToDate': "You're up to date", 'upd.retry': 'Retry', 'upd.browser': 'Open page', 'upd.checking': 'Checking for updates…', 'upd.upToDate': "You're up to date",
@@ -199,7 +199,7 @@ const en: Dict = {
'wlc.title': 'Contest', 'wlc.pattern': 'Auto-add on', 'wlc.patternPh': 'WWA', 'wlc.patternHint': 'any spotted callsign CONTAINING this joins the watchlist as a contest entry (TM29WWA, HB9WWA, F4WWA/P)', 'wlc.title': 'Contest', 'wlc.pattern': 'Auto-add on', 'wlc.patternPh': 'WWA', 'wlc.patternHint': 'any spotted callsign CONTAINING this joins the watchlist as a contest entry (TM29WWA, HB9WWA, F4WWA/P)',
'wlc.calls': 'And these callsigns, one per line', 'wlc.callsPh': 'R7W DL0ABC', 'wlc.callsHint': 'For the entries a pattern cannot catch: a station taking part under a callsign that says nothing about the event. Named here, it joins the contest watchlist the moment it is spotted. Commas and spaces work too.', 'wlc.calls': 'And these callsigns, one per line', 'wlc.callsPh': 'R7W DL0ABC', 'wlc.callsHint': 'For the entries a pattern cannot catch: a station taking part under a callsign that says nothing about the event. Named here, it joins the contest watchlist the moment it is spotted. Commas and spaces work too.',
// FTx decodes panel (Tools -> FT decodes) // FTx decodes panel (Tools -> FT decodes)
'ftmap.tab': 'FT Map', 'ftmap.noGrid': 'Set your station grid in Preferences to place the map.', 'dec.tab': 'FT decodes', 'dec.title': 'Decodes', 'dec.bandDrift': '{app} says {dec}, the rig is on {rig}', 'dec.bandDriftApp': 'the decoder', 'dec.bandDriftTip': 'The decoding application is announcing a band the radio is not on — it has most likely lost its CAT link and is repeating the last frequency it knew. Every decode below carries that band, so the NEW / NEW BAND verdicts are judged against it.', 'dec.cqOnly': 'CQ only', 'dec.sortTip': 'Sort this slot by this column. Click again to reverse it, once more for the order the decoder heard them in.', 'ftmap.tab': 'FT Map', 'ftmap.noGrid': 'Set your station grid in Preferences to place the map.', 'ftmap.colour': 'One colour for every decode, whatever the band', 'ftmap.colourPerBand': 'Back to the colour per band', 'dec.tab': 'FT decodes', 'dec.title': 'Decodes', 'dec.bandDrift': '{app} says {dec}, the rig is on {rig}', 'dec.bandDriftApp': 'the decoder', 'dec.bandDriftTip': 'The decoding application is announcing a band the radio is not on — it has most likely lost its CAT link and is repeating the last frequency it knew. Every decode below carries that band, so the NEW / NEW BAND verdicts are judged against it.', 'dec.cqOnly': 'CQ only', 'dec.sortTip': 'Sort this slot by this column. Click again to reverse it, once more for the order the decoder heard them in.',
'dec.allBands': 'All bands', 'dec.allModes': 'All modes', 'toast.qsoLogged': 'QSO logged', 'dec.contsHint': 'Continents: click to keep one or several', 'dec.allConts': 'All continents', 'dec.allBands': 'All bands', 'dec.allModes': 'All modes', 'toast.qsoLogged': 'QSO logged', 'dec.contsHint': 'Continents: click to keep one or several', 'dec.allConts': 'All continents',
'dec.minSnrTitle': 'Hide anything weaker than this SNR', 'dec.searchPh': 'Call, grid or message', 'dec.minSnrTitle': 'Hide anything weaker than this SNR', 'dec.searchPh': 'Call, grid or message',
'dec.clearFilters': 'Clear', 'dec.live': 'live', 'dec.tx': 'TX', 'dec.clearFilters': 'Clear', 'dec.live': 'live', 'dec.tx': 'TX',
@@ -840,7 +840,7 @@ const fr: Dict = {
'wlc.title': 'Contest', 'wlc.pattern': 'Ajout auto sur', 'wlc.patternPh': 'WWA', 'wlc.patternHint': 'tout indicatif spotté CONTENANT ceci rejoint la watchlist comme entrée contest (TM29WWA, HB9WWA, F4WWA/P)', 'wlc.title': 'Contest', 'wlc.pattern': 'Ajout auto sur', 'wlc.patternPh': 'WWA', 'wlc.patternHint': 'tout indicatif spotté CONTENANT ceci rejoint la watchlist comme entrée contest (TM29WWA, HB9WWA, F4WWA/P)',
'wlc.calls': 'Et ces indicatifs, un par ligne', 'wlc.callsPh': 'R7W DL0ABC', 'wlc.callsHint': 'Pour les participants quaucun motif ne peut attraper : une station engagée sous un indicatif qui ne dit rien de l’événement. Nommée ici, elle rejoint la watchlist contest dès quelle est spottée. Les virgules et les espaces marchent aussi.', 'wlc.calls': 'Et ces indicatifs, un par ligne', 'wlc.callsPh': 'R7W DL0ABC', 'wlc.callsHint': 'Pour les participants quaucun motif ne peut attraper : une station engagée sous un indicatif qui ne dit rien de l’événement. Nommée ici, elle rejoint la watchlist contest dès quelle est spottée. Les virgules et les espaces marchent aussi.',
// Panneau des decodes FTx (Outils -> Decodes FT) // Panneau des decodes FTx (Outils -> Decodes FT)
'ftmap.tab': 'FT Map', 'ftmap.noGrid': 'Renseigne ton locator dans les Préférences pour placer la carte.', 'dec.tab': 'Decodes FT', 'dec.title': 'Decodes', 'dec.bandDrift': '{app} annonce {dec}, le poste est sur {rig}', 'dec.bandDriftApp': 'le décodeur', 'dec.bandDriftTip': 'Le logiciel de décodage annonce une bande sur laquelle la radio nest pas — il a très probablement perdu sa liaison CAT et répète la dernière fréquence connue. Tous les décodages ci-dessous portent cette bande, et les verdicts NOUVEAU / NOUVELLE BANDE sont jugés dessus.', 'dec.cqOnly': 'CQ seulement', 'dec.sortTip': 'Trier ce créneau sur cette colonne. Un second clic inverse, un troisième rend lordre dans lequel le décodeur les a entendus.', 'ftmap.tab': 'FT Map', 'ftmap.noGrid': 'Renseigne ton locator dans les Préférences pour placer la carte.', 'ftmap.colour': 'Une seule couleur pour tous les décodages, quelle que soit la bande', 'ftmap.colourPerBand': 'Revenir à la couleur par bande', 'dec.tab': 'Decodes FT', 'dec.title': 'Decodes', 'dec.bandDrift': '{app} annonce {dec}, le poste est sur {rig}', 'dec.bandDriftApp': 'le décodeur', 'dec.bandDriftTip': 'Le logiciel de décodage annonce une bande sur laquelle la radio nest pas — il a très probablement perdu sa liaison CAT et répète la dernière fréquence connue. Tous les décodages ci-dessous portent cette bande, et les verdicts NOUVEAU / NOUVELLE BANDE sont jugés dessus.', 'dec.cqOnly': 'CQ seulement', 'dec.sortTip': 'Trier ce créneau sur cette colonne. Un second clic inverse, un troisième rend lordre dans lequel le décodeur les a entendus.',
'dec.allBands': 'Toutes bandes', 'dec.allModes': 'Tous modes', 'toast.qsoLogged': 'QSO enregistré', 'dec.contsHint': 'Continents : clique pour en garder un ou plusieurs', 'dec.allConts': 'Tous continents', 'dec.allBands': 'Toutes bandes', 'dec.allModes': 'Tous modes', 'toast.qsoLogged': 'QSO enregistré', 'dec.contsHint': 'Continents : clique pour en garder un ou plusieurs', 'dec.allConts': 'Tous continents',
'dec.minSnrTitle': 'Masquer tout ce qui est plus faible que ce rapport', 'dec.searchPh': 'Indicatif, locator ou message', 'dec.minSnrTitle': 'Masquer tout ce qui est plus faible que ce rapport', 'dec.searchPh': 'Indicatif, locator ou message',
'dec.clearFilters': 'Effacer', 'dec.live': 'en direct', 'dec.tx': 'TX', 'dec.clearFilters': 'Effacer', 'dec.live': 'en direct', 'dec.tx': 'TX',
@@ -919,7 +919,7 @@ const fr: Dict = {
'awards.followHint': 'Diplômes affichés dans longlet Awards. Laisse la colonne de droite vide pour tous les afficher.', 'awards.available': 'Tous les diplômes', 'awards.followed': 'Suivis', 'awards.search': 'Filtrer…', 'awards.addAll': 'Tout ajouter', 'awards.clear': 'Vider', 'awards.allTracked': 'Tous les diplômes sont suivis.', 'awards.noneFollowed': 'Aucun pour linstant — longlet Awards les montre tous.', 'awards.followHint': 'Diplômes affichés dans longlet Awards. Laisse la colonne de droite vide pour tous les afficher.', 'awards.available': 'Tous les diplômes', 'awards.followed': 'Suivis', 'awards.search': 'Filtrer…', 'awards.addAll': 'Tout ajouter', 'awards.clear': 'Vider', 'awards.allTracked': 'Tous les diplômes sont suivis.', 'awards.noneFollowed': 'Aucun pour linstant — longlet Awards les montre tous.',
'sec.awards': 'Diplômes', 'sec.cat': 'Interface CAT', 'sec.rotator': 'Rotator', 'sec.winkeyer': 'Manipulateur CW', 'sec.awards': 'Diplômes', 'sec.cat': 'Interface CAT', 'sec.rotator': 'Rotator', 'sec.winkeyer': 'Manipulateur CW',
'sec.antenna': 'Antenne motorisée', 'sec.antgenius': 'Antenna Genius', 'sec.tunergenius': 'Tuner Genius', 'sec.pgxl': 'Amplificateur', 'sec.psu': 'Alimentation', 'sec.antenna': 'Antenne motorisée', 'sec.antgenius': 'Antenna Genius', 'sec.tunergenius': 'Tuner Genius', 'sec.pgxl': 'Amplificateur', 'sec.psu': 'Alimentation',
'upd.available': 'OpsLog v{v} disponible', 'upd.current': 'Tu es en v{v}.', 'upd.install': 'Mettre à jour maintenant', 'upd.download': 'Télécharger', 'upd.later': 'Plus tard', 'upd.downloading': 'Téléchargement…', 'upd.installing': 'Installation…', 'upd.restartNote': 'OpsLog redémarrera sur la nouvelle version.', 'upd.retry': 'Réessayer', 'upd.browser': 'Ouvrir la page', 'upd.available': 'OpsLog v{v} disponible', 'upd.current': 'Tu es en v{v}.', 'upd.install': 'Mettre à jour maintenant', 'upd.download': 'Télécharger', 'upd.later': 'Plus tard', 'upd.remindIn': 'Me rappeler dans', 'upd.dismissHour': 'Masquer pendant une heure', 'upd.downloading': 'Téléchargement…', 'upd.installing': 'Installation…', 'upd.restartNote': 'OpsLog redémarrera sur la nouvelle version.', 'upd.retry': 'Réessayer', 'upd.browser': 'Ouvrir la page',
'psu.title': 'Alimentation de laboratoire (Modbus RTU)', 'psu.hint': 'Une alimentation programmable sur port série — BSIDE, Wanptek et les autres alimentations Modbus RTU utilisant les codes fonction 03 et 06. OpsLog affiche ce quelle débite et commute sa sortie.', 'psu.enable': 'Piloter cette alimentation', 'psu.port': 'Port COM', 'psu.baud': 'Vitesse', 'psu.address': 'Adresse Modbus', 'psu.wireHint': '9600 bauds, 8 bits de données, sans parité, 1 bit de stop, adresse 1 — les réglages dusine. Ne les changez ici que si vous les avez changés sur lalimentation.', 'psu.writeScope': 'OpsLog n’écrit jamais que la marche/arrêt de la sortie. La tension, le courant et les protections sont lus et affichés, jamais modifiés — ils restent sur la face avant de lalimentation.', 'psu.output': 'Sortie', 'psu.setTo': 'réglée sur', 'psu.tripped': 'PROTECTION DÉCLENCHÉE', 'psu.offline': 'Ne répond pas', 'psu.title': 'Alimentation de laboratoire (Modbus RTU)', 'psu.hint': 'Une alimentation programmable sur port série — BSIDE, Wanptek et les autres alimentations Modbus RTU utilisant les codes fonction 03 et 06. OpsLog affiche ce quelle débite et commute sa sortie.', 'psu.enable': 'Piloter cette alimentation', 'psu.port': 'Port COM', 'psu.baud': 'Vitesse', 'psu.address': 'Adresse Modbus', 'psu.wireHint': '9600 bauds, 8 bits de données, sans parité, 1 bit de stop, adresse 1 — les réglages dusine. Ne les changez ici que si vous les avez changés sur lalimentation.', 'psu.writeScope': 'OpsLog n’écrit jamais que la marche/arrêt de la sortie. La tension, le courant et les protections sont lus et affichés, jamais modifiés — ils restent sur la face avant de lalimentation.', 'psu.output': 'Sortie', 'psu.setTo': 'réglée sur', 'psu.tripped': 'PROTECTION DÉCLENCHÉE', 'psu.offline': 'Ne répond pas',
'sec.flex': 'FlexRadio', 'sec.audio': 'Périphériques audio', 'sec.flex': 'FlexRadio', 'sec.audio': 'Périphériques audio',
// Panneau Manipulateur CW // Panneau Manipulateur CW