import { useMemo, useState } from "react"; 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 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("Server URL must use http or https"); } const saved = await shellApi.saveProfile({ id: isAddingConnection ? undefined : (editingProfileId ?? editingProfile?.id), name: workingName || "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 (

Connection Manager

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

No remote servers saved yet.

{shellState.host === "mobile-shell" && ( )}
) : ( shellState.profiles.map((profile) => (
{profile.name}
{profile.serverUrl}
{profile.id === shellState.activeProfileId && 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 && (

Delete {deleteCandidate.name}? This removes the saved profile.

)}
); }