style(icom): the shared LED meters, and the sideband named

The console's thin home-made bars were the one holdout — every other panel
(Flex, Elecraft, the amps, the tuner) draws the same MeterBar. And the mode
badge said SSB, which on any band leaves the operator guessing which
sideband the rig is on: the panel now shows USB or LSB (the log keeps ADIF's
SSB).
This commit is contained in:
2026-08-30 03:00:58 +02:00
parent dad762a0f1
commit 605c934d33
3 changed files with 39 additions and 10 deletions
+22 -8
View File
@@ -284,6 +284,13 @@ function Meter({ label, value, accent, scale, onClick, title }: { label: string;
// sParts turns the raw 0-100 S-meter into S-unit + dB-over-S9 (S9 ≈ 47% on the
// CI-V 0-255 scale, +60 dB near full scale). Used for both the display label and
// the RST-tx value on click.
// Green to S9, amber through +20, red above — the Elecraft console's scale.
function sSegColor(frac: number) {
if (frac > 0.78) return '#dc2626';
if (frac > 0.55) return '#f59e0b';
return '#16a34a';
}
function sParts(v: number): { s: number; over: number; label: string } {
if (v >= 47) {
const over = Math.max(0, Math.round((v - 47) * 60 / 47));
@@ -420,7 +427,9 @@ export function IcomPanel({ onReportRST, isNetwork = false }: { onReportRST?: (r
// in split the CAT state's TX freq is the authority, otherwise the panel's
// own sub_hz read.
const subHz: number = split ? (cat?.freq_hz || 0) : (st.sub_hz || 0);
const curMode: string = cat?.mode || st.mode || '';
// The panel's own mode first: it carries the sideband (USB/LSB) where the
// CAT state folds both into ADIF's SSB.
const curMode: string = st.mode || cat?.mode || '';
// Mode-dependent controls: VOX / speech-comp / mic are voice-only (hidden on
// CW and data); APF (audio peak filter) is CW-only. Fold USB/LSB into phone.
const um = curMode.toUpperCase();
@@ -512,21 +521,26 @@ export function IcomPanel({ onReportRST, isNetwork = false }: { onReportRST?: (r
</div>
</div>
{/* Live meters — always visible: S (RX, click → RST), Po in watts, SWR. */}
<div className="rounded-xl border border-border bg-card px-3 py-2.5 shadow-sm grid grid-cols-1 sm:grid-cols-3 gap-x-5 gap-y-2">
{/* Live meters — the SAME LED MeterBar every other console uses (Flex,
Elecraft, the amp cards): one instrument look across the app, per the
operator's "les consoles doivent se ressembler". S is clickable → RST. */}
<div className="grid grid-cols-1 sm:grid-cols-3 gap-2">
{(() => { const sp = sParts(st.s_meter); return (
<Meter label="S" value={st.s_meter} accent="#22c55e" scale={sp.label}
<MeterBar label="S-METER" value={st.transmitting ? 0 : st.s_meter} lo={0} hi={100}
accent="#16a34a" segColor={sSegColor}
display={st.transmitting ? '—' : sp.label}
title={onReportRST ? t('rst.clickToFill') : undefined}
onClick={onReportRST ? () => onReportRST(sMeterRST(sp.s, sp.over, st.mode)) : undefined} />
onClick={onReportRST ? () => { if (!st.transmitting) onReportRST(sMeterRST(sp.s, sp.over, st.mode)); } : undefined} />
); })()}
{(() => {
if ((st.model ?? '').includes('7760')) {
const { w, defl } = icomWatts(st.power_meter);
return <Meter label="Po" value={defl} accent="#ef4444" scale={`${w} W`} />;
return <MeterBar label="PWR" value={defl} lo={0} hi={100} accent="#0ea5e9" display={`${w} W`} />;
}
return <Meter label="Po" value={st.power_meter} accent="#ef4444" scale={`${st.power_meter} W`} />;
return <MeterBar label="PWR" value={st.power_meter} lo={0} hi={100} accent="#0ea5e9" display={`${st.power_meter} W`} />;
})()}
<Meter label="SWR" value={st.swr_meter} accent="#f59e0b" scale={st.swr_meter > 0 ? `${(1 + st.swr_meter / 33.3).toFixed(1)}` : '1.0'} />
<MeterBar label="SWR" value={st.swr_meter > 0 ? 1 + st.swr_meter / 33.3 : 0} lo={1} hi={4} accent="#f59e0b"
display={st.swr_meter > 0 ? (1 + st.swr_meter / 33.3).toFixed(1) : '—'} />
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-3">