import React, { useState, useEffect } from 'react'; import { Lock, Unlock, KeyRound, ShieldCheck, AlertCircle, Sparkles, Sliders, ChevronDown, ChevronUp, Eye, EyeOff, LogOut, Globe, CheckCircle2, Copy, ExternalLink } from 'lucide-react'; import { useVault } from '../context/VaultContext.js'; export const LockScreen: React.FC = () => { const { did, isInitialized, initializeVault, unlockVault, loadDemoVault, isLoading, error, kdfSettings, setKdfSettings, oauthSession, isOAuthLoading, isAtprotoConnected, handle, spaceUri, connectWithOAuth, disconnectOAuth } = useVault(); const [identifier, setIdentifier] = useState('https://spaces-alpha.host.bsky.network'); const [mode, setMode] = useState<'unlock' | 'create'>('unlock'); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [showPassword, setShowPassword] = useState(false); const [showKdfSettings, setShowKdfSettings] = useState(false); const [localError, setLocalError] = useState(null); const [copiedDid, setCopiedDid] = useState(false); const [isSigningIn, setIsSigningIn] = useState(false); useEffect(() => { if (isInitialized) { setMode('unlock'); } else { setMode('create'); } }, [isInitialized]); const handleOAuthConnect = async (e: React.FormEvent) => { e.preventDefault(); if (!identifier.trim()) return; setLocalError(null); setIsSigningIn(true); try { await connectWithOAuth(identifier.trim()); } catch (err: any) { setLocalError(err.message || 'Failed to connect via OAuth'); } finally { setIsSigningIn(false); } }; const handleVaultSubmit = async (e: React.FormEvent) => { e.preventDefault(); setLocalError(null); if (!password) { setLocalError('Please enter your master password.'); return; } try { if (mode === 'create') { if (password.length < 8) { setLocalError('Master password must be at least 8 characters long.'); return; } if (password !== confirmPassword) { setLocalError('Master passwords do not match.'); return; } await initializeVault(password, kdfSettings); } else { await unlockVault(password); } } catch (err: any) { console.warn('Authentication attempt failed:', err); } }; const handleCopyDid = () => { navigator.clipboard.writeText(did); setCopiedDid(true); setTimeout(() => setCopiedDid(false), 2000); }; const handleDemoVault = async () => { setLocalError(null); try { await loadDemoVault(); } catch (err: any) { setLocalError(err.message || 'Failed to load demo vault'); } }; const currentDisplayError = localError || error; return (

2password

Zero-Knowledge Vault natively secured by AT Protocol & Spaces

{currentDisplayError && (
Notice {currentDisplayError}
)} {!isAtprotoConnected && !did.includes('demo') ? (

Connect with AT Protocol

Log in via ATProto OAuth to securely link your Space and derive your identity salt.

setIdentifier(e.target.value)} placeholder="alice.bsky.social or https://spaces-alpha.host.bsky.network" className="w-full px-3.5 py-2.5 bg-slate-950/60 border border-slate-700/80 rounded-xl text-sm text-slate-200 placeholder-slate-600 focus:outline-none focus:ring-2 focus:ring-cyan-500/50 focus:border-cyan-500 transition-colors" required />
Quick connect:
Or
) : (
{handle ? `@${handle}` : 'ATProto Connected'}
{did}
{spaceUri && (
đŸ”’ Space: {typeof spaceUri === 'string' ? spaceUri.split('/').pop() : ''}
)}
Zero-Knowledge AES-256
setPassword(e.target.value)} placeholder={mode === 'create' ? 'Create a strong master password' : 'Enter your master password'} className="w-full pl-9 pr-10 py-2.5 bg-slate-950/60 border border-slate-700/80 rounded-xl text-sm text-slate-200 placeholder-slate-600 focus:outline-none focus:ring-2 focus:ring-cyan-500/50 focus:border-cyan-500 transition-colors" required />
{mode === 'create' && (
setConfirmPassword(e.target.value)} placeholder="Repeat master password" className="w-full pl-9 pr-3 py-2.5 bg-slate-950/60 border border-slate-700/80 rounded-xl text-sm text-slate-200 placeholder-slate-600 focus:outline-none focus:ring-2 focus:ring-cyan-500/50 focus:border-cyan-500 transition-colors" required />
)}
{showKdfSettings && (
Iterations (Time cost):
setKdfSettings((prev) => ({ ...prev, iterations: Number(e.target.value) })) } className="w-24 accent-cyan-500" /> {kdfSettings.iterations}
Memory (KiB):
Parallelism (Lanes): {kdfSettings.parallelism}
)}
)}

Encrypted client-side with AES-256-GCM. Master keys never leave this browser.

); };