// Tiny "change password" drawer shown from the topbar button. function Profile({ open, onClose }) { const [current, setCurrent] = React.useState(''); const [next, setNext] = React.useState(''); const [confirm, setConfirm] = React.useState(''); const [busy, setBusy] = React.useState(false); const [msg, setMsg] = React.useState(null); React.useEffect(() => { if (open) { setCurrent(''); setNext(''); setConfirm(''); setMsg(null); setBusy(false); } }, [open]); async function submit(e) { e.preventDefault(); setMsg(null); if (!next || next.length < 6) { setMsg({ kind: 'err', text: 'new password must be at least 6 characters' }); return; } if (next !== confirm) { setMsg({ kind: 'err', text: 'new password and confirm do not match' }); return; } setBusy(true); try { await window.apiFetch('/api/auth/password', { method: 'POST', body: JSON.stringify({ current, new: next }), }); setMsg({ kind: 'ok', text: 'password updated — other sessions signed out' }); setCurrent(''); setNext(''); setConfirm(''); } catch (e2) { setMsg({ kind: 'err', text: e2.message || 'failed' }); } finally { setBusy(false); } } if (!open) return
; const INPUT = { width: '100%', padding: '6px 10px', background: 'var(--surface)', border: '1px solid var(--border-2)', borderRadius: 2, color: 'var(--text)', fontFamily: 'var(--mono)', fontSize: 12, outline: 'none', }; return (
Change password
updates your sign-in credential
setCurrent(e.target.value)} autoFocus required />
setNext(e.target.value)} required />
setConfirm(e.target.value)} required />
{msg && (
{msg.kind === 'ok' ? '✓' : '✗'} {msg.text}
)}
); } Object.assign(window, { Profile });