import { useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import type { FusionShellApi, ShellConnectionProfile, ShellConnectionState } from "../types/native-shell"; import "./NativeShellConnectionManager.css"; interface NativeShellConnectionManagerProps { open: boolean; shellApi: FusionShellApi; shellState: ShellConnectionState; onClose: () => void; } export function NativeShellConnectionManager({ open, shellApi, shellState, onClose }: NativeShellConnectionManagerProps) { const { t } = useTranslation("app"); const activeProfile = useMemo( () => shellState.profiles.find((profile) => profile.id === shellState.activeProfileId) ?? null, [shellState.activeProfileId, shellState.profiles], ); const [editingProfileId, setEditingProfileId] = useState(null); const [draft, setDraft] = useState>({}); const [deleteCandidate, setDeleteCandidate] = useState(null); const [error, setError] = useState(null); if (!open) return null; const isAddingConnection = editingProfileId === "__new__"; const editingProfile = isAddingConnection ? null : shellState.profiles.find((profile) => profile.id === editingProfileId) ?? activeProfile; const workingName = draft.name ?? editingProfile?.name ?? ""; const workingUrl = draft.serverUrl ?? editingProfile?.serverUrl ?? ""; const workingToken = draft.authToken ?? editingProfile?.authToken ?? ""; const resetEditor = () => { setEditingProfileId(null); setDraft({}); setError(null); }; const saveCurrent = async () => { setError(null); try { const parsed = new URL(workingUrl.trim()); if (!/^https?:$/.test(parsed.protocol)) { throw new Error(t("shell.serverUrlProtocolError", "Server URL must use http or https")); } const saved = await shellApi.saveProfile({ id: isAddingConnection ? undefined : (editingProfileId ?? editingProfile?.id), name: workingName || t("shell.defaultProfileName", "Remote Server"), serverUrl: workingUrl, authToken: workingToken || null, }); await shellApi.setActiveProfile(saved.id); setEditingProfileId(saved.id); setDraft({}); } catch (nextError) { setError((nextError as Error).message); } }; const handleScanQr = async () => { setError(null); try { const result = await shellApi.startQrScan(); setEditingProfileId("__new__"); setDraft({ name: "", serverUrl: result.serverUrl, authToken: result.authToken ?? "", }); } catch (nextError) { setError((nextError as Error).message); } }; const handleConfirmDelete = async () => { if (!deleteCandidate) { return; } await shellApi.deleteProfile(deleteCandidate.id); setDeleteCandidate(null); resetEditor(); }; return (

{t("shell.connectionManager", "Connection Manager")}

{shellState.host === "desktop-shell" && (
)}
{shellState.profiles.length === 0 ? (

{t("shell.noServersSaved", "No remote servers saved yet.")}

{shellState.host === "mobile-shell" && ( )}
) : ( shellState.profiles.map((profile) => (
{profile.name}
{profile.serverUrl}
{profile.id === shellState.activeProfileId && {t("shell.activePill", "Active")}}
)) )}
{shellState.host === "mobile-shell" && ( )}
setDraft((value) => ({ ...value, name: event.target.value }))} /> setDraft((value) => ({ ...value, serverUrl: event.target.value }))} /> setDraft((value) => ({ ...value, authToken: event.target.value }))} /> {error &&

{error}

}
{deleteCandidate && (

{t("shell.deleteConfirmMessage", "Delete {{name}}? This removes the saved profile.", { name: deleteCandidate.name })}

)}
); }