diff --git a/changelog.json b/changelog.json index 95c31a3..adc67ea 100644 --- a/changelog.json +++ b/changelog.json @@ -6,13 +6,15 @@ "[NEW] Typing a digital watering hole sets the mode with it. A spot click has always carried one; a frequency typed by hand carried none, so the rig stayed in SSB on 28.074 while the operator waited for decodes. Same table and same tolerance as a spot (±3 kHz of a known FT8/FT4/JS8 frequency), only towards the digital modes: tuning away from one leaves the mode alone, because there the frequency says nothing about what you mean to do.", "Yaesu CAT now drives the older radios. The FTDX10, FT-991A, FT-891 and FT-710 write a frequency in nine digits; everything before them — FTDX3000, FTDX5000, FTDX1200, FT-2000, FT-950, FT-450 — writes eight and answers a nine-digit command with a rejection, which is what an FTDX3000 owner saw: every FA refused and a radio that would not follow. The width is taken from the rig’s own reply rather than from a table of models, so a set is in the format that radio speaks — including models this backend has never heard of.", "Yaesu: RTTY can be set on USB (Settings → CAT). ADIF records only “RTTY” and the rig has both sidebands, so the log cannot answer for it — the older RTTY-L stays the default, and a station whose FSK controller wants the upper one says so once.", - "The update no longer relaunches OpsLog through a hidden PowerShell. An unsigned program that replaces itself on disk, clears the mark-of-the-web and then spawns a windowless PowerShell to start another executable is — byte for byte — the shape of a dropper, and Windows Defender’s machine-learning model reads the shape, not the intention: 0.27.14 was removed from a station under Trojan:Script/Wacatac.H!ml. The new version simply starts itself and waits its turn on the single-instance lock, which it already knew how to do. Only the rare fallback path, when the running file cannot even be renamed, still needs a helper that outlives the process." + "The update no longer relaunches OpsLog through a hidden PowerShell. An unsigned program that replaces itself on disk, clears the mark-of-the-web and then spawns a windowless PowerShell to start another executable is — byte for byte — the shape of a dropper, and Windows Defender’s machine-learning model reads the shape, not the intention: 0.27.14 was removed from a station under Trojan:Script/Wacatac.H!ml. The new version simply starts itself and waits its turn on the single-instance lock, which it already knew how to do. Only the rare fallback path, when the running file cannot even be renamed, still needs a helper that outlives the process.", + "Rotor widget: with more than one rotor the panel no longer runs off the bottom. The selector row appears above the dial, and the widget’s height is not its own to take — it sits in a strip sized by the entry form beside it — so the SP/LP pair and half the Stop button were cut off. The dial, the button rows and the padding now give that row back between them, in proportion, and nothing is dropped." ], "fr": [ "[NEW] Taper une fréquence d’appel numérique règle le mode avec elle. Un clic sur un spot en portait un depuis toujours ; une fréquence tapée à la main n’en portait aucun, si bien que le poste restait en SSB sur 28.074 pendant qu’on attendait les décodages. Même table et même tolérance qu’un spot (±3 kHz d’une fréquence FT8/FT4/JS8 connue), et seulement vers les modes numériques : en s’en éloignant le mode n’est pas touché, car là la fréquence ne dit rien de ce qu’on veut faire.", "Le CAT Yaesu pilote désormais les postes plus anciens. FTDX10, FT-991A, FT-891 et FT-710 écrivent une fréquence sur neuf chiffres ; tout ce qui précède — FTDX3000, FTDX5000, FTDX1200, FT-2000, FT-950, FT-450 — l’écrit sur huit et rejette une commande à neuf chiffres. C’est ce que voyait un possesseur de FTDX3000 : chaque FA refusée et une radio qui ne suivait pas. Le format est pris dans la réponse du poste plutôt que dans une table de modèles : l’envoi part donc dans la langue de cette radio-là, y compris pour des modèles que ce backend ne connaît pas.", "Yaesu : le RTTY peut être placé en USB (Réglages → CAT). L’ADIF n’enregistre que « RTTY » et le poste a les deux bandes latérales : le log ne peut pas répondre à sa place. Le RTTY-L ancien reste par défaut, et une station dont l’interface FSK veut la supérieure le dit une fois.", - "La mise à jour ne relance plus OpsLog par un PowerShell caché. Un programme non signé qui se remplace sur le disque, efface la marque « téléchargé depuis Internet » puis lance un PowerShell sans fenêtre pour démarrer un autre exécutable a — à l’octet près — la forme d’un dropper, et le modèle d’apprentissage de Windows Defender lit la forme, pas l’intention : la 0.27.14 a été supprimée chez un OM sous Trojan:Script/Wacatac.H!ml. La nouvelle version se lance elle-même et attend son tour sur le verrou d’instance unique, ce qu’elle savait déjà faire. Seul le repli rare, quand le fichier en cours d’exécution ne peut même pas être renommé, garde un assistant qui survit au processus." + "La mise à jour ne relance plus OpsLog par un PowerShell caché. Un programme non signé qui se remplace sur le disque, efface la marque « téléchargé depuis Internet » puis lance un PowerShell sans fenêtre pour démarrer un autre exécutable a — à l’octet près — la forme d’un dropper, et le modèle d’apprentissage de Windows Defender lit la forme, pas l’intention : la 0.27.14 a été supprimée chez un OM sous Trojan:Script/Wacatac.H!ml. La nouvelle version se lance elle-même et attend son tour sur le verrou d’instance unique, ce qu’elle savait déjà faire. Seul le repli rare, quand le fichier en cours d’exécution ne peut même pas être renommé, garde un assistant qui survit au processus.", + "Widget rotor : avec plusieurs rotors, le panneau ne déborde plus par le bas. La rangée de sélection apparaît au-dessus du cadran, et la hauteur du widget ne lui appartient pas — il occupe une bande dont la hauteur est fixée par la saisie à côté — si bien que la paire SP/LP et la moitié du bouton Stop se retrouvaient coupées. Le cadran, les rangées de boutons et les marges rendent désormais cette hauteur entre eux, chacun pour sa part, sans rien supprimer." ] }, { diff --git a/frontend/src/components/RotorCompass.tsx b/frontend/src/components/RotorCompass.tsx index 52e5fd0..b4e18de 100644 --- a/frontend/src/components/RotorCompass.tsx +++ b/frontend/src/components/RotorCompass.tsx @@ -610,9 +610,22 @@ export function RotorCompass({ return columns; }, [presets]); - // 192 dial + 6 gap + 154 controls + 16 padding, plus 60 per preset column. + // THE SELECTOR HAS TO COME OUT OF SOMEWHERE. + // + // With more than one rotor a row of buttons appears above the dial, and the + // widget's height is not its own to take: it sits in a strip whose height is + // set by the entry form beside it. The extra row simply pushed the bottom of + // the panel off the end — the SP/LP pair and half the Stop button gone. + // + // So the dial and the button rows give the row back, in proportion: 24 px off + // the dial and 8 off each of the three rows is the height of a selector, and + // nothing has to be dropped. + const tight = !!(rotors && rotors.length > 1); + const dialPx = tight ? 168 : 192; + const rowPx = tight ? 52 : 60; + // 6 gap + 154 controls + 16 padding, plus 60 per preset column. const controlsWidth = 154 + presetColumns.length * 60; - const widgetWidth = 368 + presetColumns.length * 60; + const widgetWidth = dialPx + 176 + presetColumns.length * 60; const markMovementCommanded = () => { movementSeenRef.current = false; @@ -713,7 +726,8 @@ export function RotorCompass({ const renderPresetColumn = (column: RotorPreset[], columnIndex: number) => (
+ className="w-[54px] min-w-[54px] shrink-0 grid gap-1.5 min-h-0" + style={{ gridTemplateRows: `repeat(3, ${rowPx}px)` }}> {[0, 2, 4].map((row) => (
{column[row] && renderPresetButton(column[row], columnIndex * 6 + row)} @@ -749,7 +763,8 @@ export function RotorCompass({ ); const mainControls = ( -
+
{/* Where the antenna is, and under it — smaller, yellow, and only while it matters — where it was told to go. */}
@@ -760,7 +775,8 @@ export function RotorCompass({ it. The green one does not move, so nothing jumps when the mouse leaves the dial. */}
{rotors && rotors.length > 1 && ( -
+
{rotors.map((name, index) => { const active = (activeRotor ?? 0) === index; const label = name?.trim() || `Rotor ${index + 1}`; @@ -922,8 +939,10 @@ export function RotorCompass({ )} {showControls ? ( -
-
{dial}
+ // The padding gives its share too: four pixels, which is what the + // selector row still owed after the dial and the buttons had paid. +
+
{dial}
{mainControls}