From 3b8ac35a7069155447d725f2dbcc2e8e78a191a1 Mon Sep 17 00:00:00 2001 From: jack Date: Thu, 30 Jul 2026 00:11:24 +0800 Subject: [PATCH] fix(web): theme saved SSH alias selector Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- web/src/components/RemoteConnectWizard.tsx | 177 +++++++++++++++++++-- 1 file changed, 166 insertions(+), 11 deletions(-) diff --git a/web/src/components/RemoteConnectWizard.tsx b/web/src/components/RemoteConnectWizard.tsx index cc13149f..90674e39 100644 --- a/web/src/components/RemoteConnectWizard.tsx +++ b/web/src/components/RemoteConnectWizard.tsx @@ -10,6 +10,7 @@ import { ArrowLeftIcon, ArrowPathIcon, CheckIcon, + ChevronDownIcon, ChevronRightIcon, CubeIcon, FolderIcon, @@ -55,9 +56,11 @@ export function RemoteConnectWizard({ open, prefill, onClose, onBound }: RemoteC const [aliasName, setAliasName] = useState('') const [error, setError] = useState('') const [binding, setBinding] = useState(false) + const [aliasMenuOpen, setAliasMenuOpen] = useState(false) /** Keep prefill for bind-time loadTaskUuid without re-running open effect. */ const prefillRef = useRef(null) const boundRef = useRef(false) + const aliasMenuRef = useRef(null) const steps = useMemo( () => [ @@ -73,6 +76,13 @@ export function RemoteConnectWizard({ open, prefill, onClose, onBound }: RemoteC return steps.findIndex((s) => s.key === key) }, [step, steps]) + const selectedAliasLabel = useMemo(() => { + if (!selectedAlias) return t('settings.ssh.noAlias') + const alias = aliases.find((a) => a.name === selectedAlias) + if (!alias) return selectedAlias + return `${alias.name} — ${alias.addr}` + }, [selectedAlias, aliases, t]) + useEffect(() => { if (!open) return boundRef.current = false @@ -92,6 +102,24 @@ export function RemoteConnectWizard({ open, prefill, onClose, onBound }: RemoteC // eslint-disable-next-line react-hooks/exhaustive-deps }, [open, prefill]) + useEffect(() => { + if (!aliasMenuOpen) return + function onDown(e: MouseEvent) { + if (aliasMenuRef.current && !aliasMenuRef.current.contains(e.target as Node)) { + setAliasMenuOpen(false) + } + } + function onKey(e: KeyboardEvent) { + if (e.key === 'Escape') setAliasMenuOpen(false) + } + document.addEventListener('mousedown', onDown) + document.addEventListener('keydown', onKey) + return () => { + document.removeEventListener('mousedown', onDown) + document.removeEventListener('keydown', onKey) + } + }, [aliasMenuOpen]) + if (!open) return null function resetForm() { @@ -106,6 +134,7 @@ export function RemoteConnectWizard({ open, prefill, onClose, onBound }: RemoteC setPassphrase('') setAliases([]) setSelectedAlias('') + setAliasMenuOpen(false) setContainers([]) setConnectionId('') setCurrentDir('') @@ -154,6 +183,7 @@ export function RemoteConnectWizard({ open, prefill, onClose, onBound }: RemoteC function applyAlias(name: string) { setSelectedAlias(name) + setAliasMenuOpen(false) const alias = aliases.find((a) => a.name === name) if (!alias) return const at = alias.addr.indexOf('@') @@ -457,17 +487,57 @@ export function RemoteConnectWizard({ open, prefill, onClose, onBound }: RemoteC {aliases.length > 0 && (
- + {/* + Custom listbox (not native