feat: While closing OpsLog will keep the same size and position for next launch

This commit is contained in:
2026-07-15 22:03:42 +02:00
parent d354709939
commit 5b96f53930
7 changed files with 1053 additions and 271 deletions
+80 -24
View File
@@ -261,7 +261,7 @@ const SECTION_LABELS: Partial<Record<SectionId, string>> = {
cat: 'CAT interface',
rotator: 'PstRotator',
winkeyer: 'CW Keyer',
antenna: 'UltraBeam',
antenna: 'Ultrabeam / Steppir',
antgenius: 'Antenna Genius',
pgxl: 'Power Genius',
flex: 'FlexRadio',
@@ -826,9 +826,9 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
const [rotatorTesting, setRotatorTesting] = useState(false);
const [rotatorTest, setRotatorTest] = useState<{ ok: boolean; msg: string } | null>(null);
// Ultrabeam antenna (TCP) settings.
const [ultrabeam, setUltrabeam] = useState<{ enabled: boolean; host: string; port: number; follow: boolean; step_khz: number }>({
enabled: false, host: '', port: 23, follow: false, step_khz: 50,
// Motorized antenna (Ultrabeam TCP or SteppIR TCP/serial) settings.
const [ultrabeam, setUltrabeam] = useState<{ enabled: boolean; type: string; transport: string; host: string; port: number; com: string; baud: number; follow: boolean; step_khz: number }>({
enabled: false, type: 'ultrabeam', transport: 'tcp', host: '', port: 23, com: '', baud: 9600, follow: false, step_khz: 50,
});
const [ubTesting, setUbTesting] = useState(false);
const [ubTest, setUbTest] = useState<{ ok: boolean; msg: string } | null>(null);
@@ -2267,36 +2267,92 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
}
function UltrabeamPanel() {
const isSteppir = ultrabeam.type === 'steppir';
const isSerial = isSteppir && ultrabeam.transport === 'serial';
return (
<>
<SectionHeader
title={t('hw.ultrabeam')}
title={t('hw.motorAntenna')}
/>
<div className="space-y-4 max-w-xl">
<label className="flex items-center gap-2 text-sm cursor-pointer">
<Checkbox checked={ultrabeam.enabled} onCheckedChange={(c) => setUltrabeam((s) => ({ ...s, enabled: !!c }))} />
Enable Ultrabeam control
{t('hw.motorEnable')}
</label>
<div className="grid grid-cols-3 gap-3">
<div className="space-y-1 col-span-2">
<Label>Host / IP</Label>
<Input
value={ultrabeam.host ?? ''}
onChange={(e) => setUltrabeam((s) => ({ ...s, host: e.target.value }))}
placeholder="192.168.1.50"
className="font-mono"
/>
</div>
<div className="grid grid-cols-2 gap-3">
<div className="space-y-1">
<Label>TCP port</Label>
<Input
type="number" min={1} max={65535}
value={ultrabeam.port}
onChange={(e) => setUltrabeam((s) => ({ ...s, port: parseInt(e.target.value) || 23 }))}
className="font-mono"
/>
<Label>{t('hw.motorType')}</Label>
{/* Ultrabeam is TCP only; picking it forces the transport back to TCP
so the serial fields never apply to it. */}
<Select value={ultrabeam.type ?? 'ultrabeam'}
onValueChange={(v) => setUltrabeam((s) => ({ ...s, type: v, transport: v === 'ultrabeam' ? 'tcp' : s.transport }))}>
<SelectTrigger className="h-9"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="ultrabeam">Ultrabeam</SelectItem>
<SelectItem value="steppir">SteppIR</SelectItem>
</SelectContent>
</Select>
</div>
{isSteppir && (
<div className="space-y-1">
<Label>{t('hw.motorTransport')}</Label>
<Select value={ultrabeam.transport ?? 'tcp'}
onValueChange={(v) => setUltrabeam((s) => ({ ...s, transport: v }))}>
<SelectTrigger className="h-9"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="tcp">{t('hw.motorTcp')}</SelectItem>
<SelectItem value="serial">{t('hw.motorSerial')}</SelectItem>
</SelectContent>
</Select>
</div>
)}
</div>
{isSerial ? (
<div className="grid grid-cols-3 gap-3">
<div className="space-y-1 col-span-2">
<Label>{t('hw.motorCom')}</Label>
<Input
value={ultrabeam.com ?? ''}
onChange={(e) => setUltrabeam((s) => ({ ...s, com: e.target.value }))}
placeholder="COM3"
className="font-mono"
/>
</div>
<div className="space-y-1">
<Label>{t('hw.motorBaud')}</Label>
<Select value={String(ultrabeam.baud || 9600)} onValueChange={(v) => setUltrabeam((s) => ({ ...s, baud: parseInt(v, 10) || 9600 }))}>
<SelectTrigger className="h-9"><SelectValue /></SelectTrigger>
<SelectContent>
{[1200, 4800, 9600, 19200].map((b) => <SelectItem key={b} value={String(b)}>{b}</SelectItem>)}
</SelectContent>
</Select>
</div>
</div>
) : (
<div className="grid grid-cols-3 gap-3">
<div className="space-y-1 col-span-2">
<Label>Host / IP</Label>
<Input
value={ultrabeam.host ?? ''}
onChange={(e) => setUltrabeam((s) => ({ ...s, host: e.target.value }))}
placeholder="192.168.1.50"
className="font-mono"
/>
</div>
<div className="space-y-1">
<Label>TCP port</Label>
<Input
type="number" min={1} max={65535}
value={ultrabeam.port}
onChange={(e) => setUltrabeam((s) => ({ ...s, port: parseInt(e.target.value) || 23 }))}
className="font-mono"
/>
</div>
</div>
)}
{isSteppir && (
<p className="text-xs text-muted-foreground">{t('hw.steppirHint')}</p>
)}
<div className="border-t border-border/60 pt-3 space-y-2">
<label className="flex items-center gap-2 text-sm cursor-pointer">
<Checkbox checked={ultrabeam.follow} onCheckedChange={(c) => setUltrabeam((s) => ({ ...s, follow: !!c }))} />
@@ -2318,7 +2374,7 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
)}
</div>
<div className="flex items-center gap-2 pt-2">
<Button variant="outline" size="sm" onClick={testUltrabeam} disabled={ubTesting || !ultrabeam.host.trim()}>
<Button variant="outline" size="sm" onClick={testUltrabeam} disabled={ubTesting || (isSerial ? !ultrabeam.com.trim() : !ultrabeam.host.trim())}>
{ubTesting ? t('hw.connecting') : t('hw.testConn')}
</Button>
</div>