feat: Winkeyer

This commit is contained in:
2026-06-02 01:17:26 +02:00
parent 2eb77370e4
commit 2b4326b553
26 changed files with 3125 additions and 645 deletions
+18 -10
View File
@@ -53,9 +53,15 @@ interface Props {
mode: string;
imageUrl?: string;
onOpenImage?: () => void;
// Optional controlled active tab (so the app can switch it via keyboard).
tab?: TabName;
onTab?: (t: TabName) => void;
// When the WinKeyer is active, F1-F12 fire macros, so the tab shortcut is
// shown as Ctrl+F1…F5 instead of F1…F5.
keyerActive?: boolean;
}
type TabName = 'stats' | 'info' | 'awards' | 'my' | 'extended';
export type TabName = 'stats' | 'info' | 'awards' | 'my' | 'extended';
const PROP_MODES = ['NONE','AS','AUE','AUR','BS','ECH','EME','ES','F2','F2M','FAI','GWAVE','INTERNET','ION','IRL','LOS','MS','RPT','RS','SAT','TEP','TR'];
@@ -75,8 +81,9 @@ function Field({ label, span = 1, children }: { label: string; span?: 1 | 2 | 3
);
}
export function DetailsPanel({ callsign: _cs, prefix, operatorGrid, remoteGrid, details, onChange, wb, wbBusy, band, mode }: Props) {
const [open, setOpen] = useState<TabName>('stats');
export function DetailsPanel({ callsign: _cs, prefix, operatorGrid, remoteGrid, details, onChange, wb, wbBusy, band, mode, tab, onTab, keyerActive }: Props) {
const [internalOpen, setInternalOpen] = useState<TabName>('stats');
const open = tab ?? internalOpen; // controlled when `tab` is provided
// Bearing/distance from operator's home grid to the remote station.
// Recomputed only when either grid actually changes.
@@ -87,7 +94,8 @@ export function DetailsPanel({ callsign: _cs, prefix, operatorGrid, remoteGrid,
const fmtDeg = (n: number) => `${Math.round(n)}°`;
const fmtKm = (n: number) => `${Math.round(n).toLocaleString()} km`;
function toggle(t: TabName) { setOpen(t); }
function toggle(t: TabName) { onTab ? onTab(t) : setInternalOpen(t); }
const fk = keyerActive ? 'Ctrl+F' : 'F';
const satelliteMode = !!details.sat_name || !!details.sat_mode || details.prop_mode === 'SAT';
function setSatellite(on: boolean) {
@@ -102,15 +110,15 @@ export function DetailsPanel({ callsign: _cs, prefix, operatorGrid, remoteGrid,
}
const tabs: { key: TabName; label: string }[] = [
{ key: 'stats', label: 'Stats (F1)' },
{ key: 'info', label: 'Info (F2)' },
{ key: 'awards', label: 'Awards (F3)' },
{ key: 'my', label: 'My (F4)' },
{ key: 'extended', label: 'Extended (F5)' },
{ key: 'stats', label: `Stats (${fk}1)` },
{ key: 'info', label: `Info (${fk}2)` },
{ key: 'awards', label: `Awards (${fk}3)` },
{ key: 'my', label: `My (${fk}4)` },
{ key: 'extended', label: `Extended (${fk}5)` },
];
return (
<section className="border border-border rounded-lg bg-card flex flex-col flex-1 min-h-0 overflow-hidden">
<section className="bg-card shadow-sm border border-border rounded-lg flex flex-col flex-1 min-h-0 overflow-hidden">
<nav className="flex items-center gap-1 px-3 bg-muted/40 border-b border-border shrink-0">
{tabs.map((t) => (
<button