feat: never drop cluster spots + theme-aware spot badges + FR settings

Backend: replace the bounded cluster-event channel (which dropped spots
under RBN bursts once enrichment/UI fell behind) with an unbounded
clusterQueue (slice + sync.Cond). The socket-read goroutine still never
blocks, but a burst now grows the backlog and drains instead of losing
spots. Head compaction keeps a sustained burst from growing the backing
array without bound.

ClusterGrid: drop all hard-coded light-theme hex colours (Time, Country,
Spotter, Comment, POTA, band/mode fills) for semantic CSS-var tokens so
they read correctly on every theme. Replace the heavy full-cell band/mode
fills with small rounded pills (cellChip). Only NEW MODE pills the mode
cell — NEW SLOT (band and mode each worked, just not together) no longer
highlights the mode cell, which wrongly implied the mode was new.

App: stage incoming spots ~50ms and resolve their slot status before
inserting, so a row paints with its badge already on instead of flashing
plain text then flipping to a pill. Self-spot toast still fires per spot.

i18n: translate the General settings that were still English — telemetry
and live-status toggles, the Main view pane pickers and their options.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
2026-07-18 18:20:06 +02:00
co-authored by Claude Opus 4.8
parent 04eaa91bd8
commit 3ed9f29d9a
5 changed files with 240 additions and 66 deletions
+22
View File
@@ -80,6 +80,17 @@ const en: Dict = {
'offline.synced': '{n} QSO(s) added to the logbook',
'offline.stillDown': 'Database still unreachable — your QSOs are safe',
'settings.theme': 'Theme', 'settings.themeHint': 'Interface colour theme.',
'settings.telemetry': 'Send anonymous usage statistics',
'settings.telemetryHint': 'install ID + version + OS, once a day — no callsign or QSO data',
'settings.liveStatus': 'Publish live operator status',
'settings.liveStatusHint': 'multi-op on shared MySQL — feeds a QRZ live page',
'settings.mainView': 'Main view',
'settings.mainViewHint': 'Choose what the Main tab shows on each side (per profile).',
'settings.leftPane': 'Left pane', 'settings.rightPane': 'Right pane',
'settings.pane.map1': 'Map — great-circle + beam', 'settings.pane.map2': 'Map — locator (street)',
'settings.pane.cluster': 'Cluster spots', 'settings.pane.worked': 'Worked before',
'settings.pane.recent': 'Recent QSOs', 'settings.pane.netcontrol': 'Net control',
'settings.pane.flex': 'FlexRadio controls', 'settings.pane.icom': 'Icom console',
'theme.auto': 'Auto (system)', 'theme.light-warm': 'Warm light', 'theme.light-cool': 'Cool light',
'theme.light-sage': 'Sage light', 'theme.dim-slate': 'Dim slate', 'theme.dark-warm': 'Warm dark',
'theme.dark-graphite': 'Graphite dark', 'theme.high-contrast': 'High contrast',
@@ -359,6 +370,17 @@ const fr: Dict = {
'offline.synced': '{n} QSO ajoutés au journal',
'offline.stillDown': 'Base toujours injoignable — tes QSO sont en sécurité',
'settings.theme': 'Thème', 'settings.themeHint': "Thème de couleur de l'interface.",
'settings.telemetry': "Envoyer des statistiques d'usage anonymes",
'settings.telemetryHint': "ID d'installation + version + OS, une fois par jour — aucun indicatif ni donnée QSO",
'settings.liveStatus': 'Publier le statut opérateur en direct',
'settings.liveStatusHint': 'multi-op sur MySQL partagé — alimente une page live QRZ',
'settings.mainView': 'Vue principale',
'settings.mainViewHint': "Choisis ce que l'onglet Principal affiche de chaque côté (par profil).",
'settings.leftPane': 'Volet gauche', 'settings.rightPane': 'Volet droit',
'settings.pane.map1': 'Carte — orthodromie + faisceau', 'settings.pane.map2': 'Carte — locator (rue)',
'settings.pane.cluster': 'Spots cluster', 'settings.pane.worked': 'Déjà contactés',
'settings.pane.recent': 'QSO récents', 'settings.pane.netcontrol': 'Gestion de net',
'settings.pane.flex': 'Contrôles FlexRadio', 'settings.pane.icom': 'Console Icom',
'theme.auto': 'Auto (système)', 'theme.light-warm': 'Clair chaud', 'theme.light-cool': 'Clair froid',
'theme.light-sage': 'Clair sauge', 'theme.dim-slate': 'Ardoise tamisé', 'theme.dark-warm': 'Sombre chaud',
'theme.dark-graphite': 'Sombre graphite', 'theme.high-contrast': 'Contraste élevé',