diff --git a/desktop/src/features/onboarding/ui/BackupStep.tsx b/desktop/src/features/onboarding/ui/BackupStep.tsx index f27192e014..e042ac8adc 100644 --- a/desktop/src/features/onboarding/ui/BackupStep.tsx +++ b/desktop/src/features/onboarding/ui/BackupStep.tsx @@ -2,8 +2,6 @@ import { Check, ChevronRight, Copy, - Eye, - EyeOff, FileLock2, ShieldCheck, } from "lucide-react"; @@ -66,9 +64,9 @@ type BackupStepProps = { /** * Onboarding identity-key step — shows the freshly created key, then opens a - * dark backup-options state. The reusable secret stays out of the DOM until an - * explicit reveal or copy action; password backup opens the separate security - * flow. + * dark backup-options state. The new key is visible by default; hovering or + * focusing the key well blurs it and replaces the key with an explicit copy + * action. Password backup opens the separate security flow. * Neither method blocks Next. */ export function BackupStep({ @@ -87,8 +85,6 @@ export function BackupStep({ >("idle"); const [copyError, setCopyError] = React.useState(null); const [nsec, setNsec] = React.useState(null); - const [isRevealed, setIsRevealed] = React.useState(false); - const [isRevealPending, setIsRevealPending] = React.useState(false); const cancelledRef = React.useRef(false); const copiedTimerRef = React.useRef(null); @@ -109,13 +105,30 @@ export function BackupStep({ React.useEffect(() => { cancelledRef.current = false; return () => { - // Back-during-fetch: cancel any in-flight setState calls. + // Back-during-fetch: cancel any in-flight setState calls and release the + // renderer's reference to the freshly generated key. cancelledRef.current = true; + setNsec(null); if (copiedTimerRef.current !== null) window.clearTimeout(copiedTimerRef.current); }; }, []); + React.useEffect(() => { + void getNsec() + .then((value) => { + if (!cancelledRef.current) setNsec(value); + }) + .catch((err: unknown) => { + if (cancelledRef.current) return; + setCopyError( + err instanceof Error + ? err.message + : "Failed to retrieve private key.", + ); + }); + }, []); + const copyKeyToClipboard = React.useCallback(async () => { setCopyState("copying"); setCopyError(null); @@ -139,35 +152,6 @@ export function BackupStep({ } }, [nsec]); - const toggleReveal = React.useCallback(async () => { - if (isRevealed) { - setIsRevealed(false); - return; - } - - setIsRevealPending(true); - setCopyError(null); - try { - const value = nsec ?? (await getNsec()); - if (cancelledRef.current) return; - setNsec(value); - setIsRevealed(true); - } catch (err) { - if (cancelledRef.current) return; - setCopyError( - err instanceof Error ? err.message : "Failed to retrieve private key.", - ); - } finally { - if (!cancelledRef.current) setIsRevealPending(false); - } - }, [isRevealed, nsec]); - - // Never read the credential merely to size its placeholder. The complete - // reusable secret enters the DOM only after an explicit Reveal action. - const maskedKey = React.useMemo( - () => Array.from({ length: nsec?.length ?? 63 }, () => "•").join("\u200b"), - [nsec], - ); const storageDescription = identityStorage === "system-keyring" ? "Buzz keeps your identity key in your system keychain. Your computer may ask for your password when Buzz needs to read the key." @@ -387,39 +371,24 @@ export function BackupStep({ >

- {isRevealed ? nsec : maskedKey} + {nsec}

+
-
{copyError ? ( diff --git a/desktop/src/features/onboarding/ui/DefaultConfigStep.tsx b/desktop/src/features/onboarding/ui/DefaultConfigStep.tsx index 06c732fb44..b3db3b91cb 100644 --- a/desktop/src/features/onboarding/ui/DefaultConfigStep.tsx +++ b/desktop/src/features/onboarding/ui/DefaultConfigStep.tsx @@ -230,7 +230,7 @@ function AgentDefaultsSection({ return (
{configSurfaceLoading ? ( @@ -424,7 +424,7 @@ export function DefaultConfigStep({