// DRAFT DESTINATION: src/popup/views/AdoptVaultView.tsx plus small hunks in // src/popup/views/Onboarding.tsx, src/popup/views/Lock.tsx, and src/popup/App.tsx. // Phase 5: "I already have Peach" receiver path. import { Match, Show, Switch, createMemo, createSignal } from "solid-js"; import { sendMessage } from "../App"; import { RuneBackground } from "../components/RuneBackground"; type AdoptPhase = "password" | "pair" | "sas" | "success" | "error"; interface AdoptVaultViewProps { onCancel(): void; onDone(): void | Promise; startAdoption(args: { password: string; confirmSAS(sas: string): Promise; createVault(rootKeyHex: string): Promise<{ vaultId: string }>; }): Promise; } const MIN_LENGTH = 12; export function AdoptVaultView(props: AdoptVaultViewProps) { const [phase, setPhase] = createSignal("password"); const [password, setPassword] = createSignal(""); const [confirmPassword, setConfirmPassword] = createSignal(""); const [sas, setSas] = createSignal(""); const [sasResolve, setSasResolve] = createSignal<((ok: boolean) => void) | null>(null); const [error, setError] = createSignal(""); const canContinue = createMemo(() => password().length >= MIN_LENGTH && confirmPassword().length >= MIN_LENGTH && password() === confirmPassword() ); function blocker(): string | null { if (!password()) return null; if (password().length < MIN_LENGTH) return `need ${MIN_LENGTH - password().length} more characters`; if (!confirmPassword()) return "confirm your password"; if (password() !== confirmPassword()) return "passwords don't match"; return null; } async function beginPairing() { setPhase("pair"); setError(""); try { await props.startAdoption({ password: password(), confirmSAS: (code) => new Promise((resolve) => { setSas(code); setSasResolve(() => resolve); setPhase("sas"); }), createVault: async (rootKeyHex) => { const res = await sendMessage({ type: "CREATE_VAULT_FROM_ROOT_KEY", payload: { password: password(), rootKeyHex }, }) as { ok: boolean; vaultId?: string; error?: string }; if (!res.ok || !res.vaultId) throw new Error(res.error ?? "adoption_create_failed"); return { vaultId: res.vaultId }; }, }); setPhase("success"); } catch (err) { setError(err instanceof Error ? err.message : String(err)); setPhase("error"); } } function confirmSas(ok: boolean) { const resolve = sasResolve(); setSasResolve(null); resolve?.(ok); if (!ok) { setPhase("password"); setPassword(""); setConfirmPassword(""); } } return (

Set a password for this device.

This protects your vault on this device only. It can be different from your other devices.

setPassword((event.target as HTMLInputElement).value)} /> setConfirmPassword((event.target as HTMLInputElement).value)} />

{blocker()}

); } // DRAFT HUNK: src/popup/views/Onboarding.tsx // - Extend props with `onAdopt?: () => void | Promise`. // - On the final card, render a third action button: // // Keep Create vault and Restore from backup visible in LockView as they are // today; this is an additional path, not a replacement. // DRAFT HUNK: src/popup/App.tsx // - Add `| "adopting"` to `View`. // - Lazy-load `AdoptVaultView`. // - Pass `onAdopt={() => setView("adopting")}` into Onboarding. // - Render AdoptVaultView while hasVault() is false. // - On success, refresh HAS_VAULT, mark onboarding complete, call handleUnlock.