fix(antenna): the COM port picks from the list again

Reported on an Ultrabeam over USB: the port could not be chosen from the
dropdown, and the panel would not scroll.

That field was a Combobox — a list you can also type into — chosen so the
port of an adapter currently UNPLUGGED could still be configured. It is
now the same Select every other serial device in OpsLog uses, the one that
demonstrably works. The case it was built for is kept differently: a
configured port that is no longer detected is added to the list, so it
stays selected and visible instead of vanishing when the adapter is out.

Also hardened the Test button's guard, which called .trim() straight on
the port and the host. A settings object without them — an older build, a
save that landed short — throws there DURING RENDER, and a panel that
throws while rendering is exactly a panel with a dead dropdown that will
not scroll. Whether or not that is what this operator hit, it is a way to
produce both symptoms at once and it costs nothing to remove.
This commit is contained in:
2026-08-26 18:21:41 +02:00
parent 37b436f543
commit 8b9c835ca1
2 changed files with 23 additions and 19 deletions
+19 -17
View File
@@ -3800,23 +3800,25 @@ function SettingsModalImpl({ onClose, onSaved, initialSection, onMainPaneChanged
<div className="grid grid-cols-3 gap-3">
<div className="space-y-1 col-span-2">
<Label>{t('hw.motorCom')}{!isSteppir && <span className="ml-1.5 font-normal text-muted-foreground">{t('hw.motorComUb')}</span>}</Label>
{/* A list AND a text field, which is why this is a Combobox and
not the Select the other serial devices use: the port for a
USB adapter that is currently unplugged does not appear in
the list, and refusing to accept it typed means the antenna
cannot be configured until the adapter is in. The detected
ports are offered; anything else is still accepted. */}
{/* THE SAME Select every other serial device uses.
This was a Combobox a list you could also type into so
that the port of an adapter currently unplugged could still
be configured. In practice the list could not be picked from
at all, which is a worse failure than the one it was avoiding:
the port an operator wants is nearly always one that is
plugged in and detected. A configured port that has since
gone missing is added to the list so it stays selected and
visible rather than silently disappearing. */}
<div className="flex items-center gap-1">
<Combobox
value={ultrabeam.com ?? ''}
options={wkPorts}
allowFreeText
commitOnType
showToggle
placeholder="COM3"
className="font-mono flex-1"
onChange={(v) => setUltrabeam((s) => ({ ...s, com: v.trim().toUpperCase() }))}
/>
<Select value={ultrabeam.com || undefined}
onValueChange={(v) => setUltrabeam((s) => ({ ...s, com: v }))}>
<SelectTrigger className="h-9 font-mono flex-1"><SelectValue placeholder="COM3" /></SelectTrigger>
<SelectContent>
{wkPorts.length === 0 && !ultrabeam.com && <SelectItem value="_" disabled>{t('cat.noPorts')}</SelectItem>}
{[...wkPorts, ...(ultrabeam.com && !wkPorts.includes(ultrabeam.com) ? [ultrabeam.com] : [])]
.map((pt) => <SelectItem key={pt} value={pt}>{pt}</SelectItem>)}
</SelectContent>
</Select>
<Button type="button" variant="outline" size="sm" className="shrink-0"
onClick={() => ListSerialPorts().then((ps) => setWkPorts((ps ?? []) as string[])).catch(() => {})}></Button>
</div>
@@ -3965,7 +3967,7 @@ function SettingsModalImpl({ onClose, onSaved, initialSection, onMainPaneChanged
<p className="text-xs text-muted-foreground pl-6">{t('hw.motorTxInhibitHint')}</p>
</div>
<div className="flex items-center gap-2 pt-2">
<Button variant="outline" size="sm" onClick={testUltrabeam} disabled={ubTesting || (isSerial ? !ultrabeam.com.trim() : !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>