diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 973707d..2606009 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -2008,17 +2008,21 @@ export default function App() { async function wkSend(rawText: string) { setWkSent(''); const resolved = resolveCW(rawText); + // Trailing word space so two macros fired back-to-back don't run together in + // the keyer buffer ("CQ" + "TEST" → "CQTEST"). The keyer keys a space as a + // word gap at the CURRENT speed, so it scales with WPM automatically. + const keyed = resolved ? resolved + ' ' : resolved; const doLog = //i.test(rawText); // resolveCW strips the token (unknown var → "") const sleep = (ms: number) => new Promise((r) => window.setTimeout(r, ms)); if (cwSourceRef.current === 'icom') { // The rig's keyer gives no busy echo back, so show the text we sent and, // for , wait the estimated send duration before logging. setWkSent(resolved); - await IcomSendCW(resolved).catch((e) => setError(String(e?.message ?? e))); + await IcomSendCW(keyed).catch((e) => setError(String(e?.message ?? e))); if (doLog) { await sleep(Math.round(estimateCwMs(resolved, wkWpm)) + 600); void save(); } return; } - await WinkeyerSend(resolved).catch((e) => setError(String(e?.message ?? e))); + await WinkeyerSend(keyed).catch((e) => setError(String(e?.message ?? e))); // (e.g. "BK 73 TU ") logs the contact AFTER the keyer has // finished sending — so the QSO isn't logged (and the form cleared) while CW // is still going out. We'd like to wait for the busy flag to rise then fall, @@ -3760,16 +3764,17 @@ export default function App() { {showQsoRate && (
- + 0 ? 'text-primary' : 'text-muted-foreground')} /> {/* Contest-style rate: QSOs/hour projected from each window - (10-min count ×6; the 60-min count is already per hour). */} + (10-min count ×6; the 60-min count is already per hour). Numbers + glow the brand accent when active, dim to muted when idle. */} 10′ - {qsoRate.last10 * 6} + 0 ? 'text-primary' : 'text-muted-foreground')}>{qsoRate.last10 * 6} 60′ - {qsoRate.last60} + 0 ? 'text-primary' : 'text-muted-foreground')}>{qsoRate.last60} Q/h
@@ -3786,6 +3791,14 @@ export default function App() { const geo = String(solar.geomag_field || '').toUpperCase(); const geoCls = /STORM|SEVERE/.test(geo) ? 'text-danger' : /ACTIVE|UNSETTLED/.test(geo) ? 'text-warning' : 'text-success'; + const num = (v: any) => { const n = Number(v); return Number.isFinite(n) ? n : null; }; + // Semantic colour by band condition: higher flux/sunspots = better HF + // (green when strong); A and K measure geomagnetic disturbance, so LOW + // is good (green quiet → yellow unsettled → red storm). + const sfiCls = (v: any) => { const n = num(v); return n == null ? 'text-foreground' : n >= 120 ? 'text-success' : n >= 90 ? 'text-foreground' : 'text-warning'; }; + const ssnCls = (v: any) => { const n = num(v); return n != null && n >= 80 ? 'text-success' : 'text-foreground'; }; + const aCls = (v: any) => { const n = num(v); return n == null ? 'text-foreground' : n <= 7 ? 'text-success' : n <= 15 ? 'text-warning' : 'text-danger'; }; + const kCls = (v: any) => { const n = num(v); return n == null ? 'text-foreground' : n <= 2 ? 'text-success' : n <= 3 ? 'text-warning' : 'text-danger'; }; const it = (label: string, val: any, cls = 'text-foreground') => ( {label} @@ -3793,10 +3806,10 @@ export default function App() { ); return (<> - {it('SFI', solar.sfi)} - {it('SSN', solar.ssn)} - {it('A', solar.a_index)} - {it('K', solar.k_index)} + {it('SFI', solar.sfi, sfiCls(solar.sfi))} + {it('SSN', solar.ssn, ssnCls(solar.ssn))} + {it('A', solar.a_index, aCls(solar.a_index))} + {it('K', solar.k_index, kCls(solar.k_index))} {geo ? {geo} : null} ); })()}