// Domain Dropdown + Add Domain modal + Manage Domains modal + Settings drawer const Badge = ({ kind }) => { const map = { TOP: { bg: '#3a2a08', color: '#F59E0B', border: '#5c3e08' }, NEW: { bg: '#0a3a33', color: '#14B8A6', border: '#0f5048' }, HOT: { bg: '#0c2a4a', color: '#60A5FA', border: '#1e3f6b' }, }; const s = map[kind]; return ( {kind} ); }; const DomainDropdown = ({ open, onClose, domains, currentDomain, onSelect, onAddDomain, onToggleFav, onShowDns, onReverify, onRemove }) => { const [query, setQuery] = React.useState(''); if (!open) return null; const filter = (list) => list.filter((d) => d.domain.includes(query.toLowerCase())); const customFiltered = filter(domains.custom); const sharedFiltered = filter(domains.shared); return ( <>
{/* Search */}
setQuery(e.target.value)} placeholder="Search domains…" style={{ flex: 1, background: 'none', border: 'none', outline: 'none', color: '#EDEDED', fontFamily: "'JetBrains Mono', monospace", fontSize: 12 }} /> ESC
{/* Custom Domains */} {customFiltered.length > 0 && (
YOUR CUSTOM DOMAINS
{customFiltered.map((d) => ( onSelect(d.domain)} onToggleFav={() => onToggleFav(d.id, 'custom')} onShowDns={onShowDns ? () => onShowDns(d) : undefined} onReverify={onReverify ? () => onReverify(d) : undefined} onRemove={onRemove ? () => onRemove(d) : undefined} /> ))}
)} {/* Shared Domains */} {sharedFiltered.length > 0 && (
0 ? '1px solid #1f1f1f' : 'none', marginTop: customFiltered.length > 0 ? 4 : 0 }}> SHARED DOMAINS · {sharedFiltered.length}
{sharedFiltered.map((d) => ( onSelect(d.domain)} onToggleFav={() => onToggleFav(d.id, 'shared')} /> ))}
)}
); }; const DomainRow = ({ d, current, onSelect, onToggleFav, onShowDns, onReverify, onRemove }) => { const [hovered, setHovered] = React.useState(false); const [menuOpen, setMenuOpen] = React.useState(false); const menuRef = React.useRef(null); // Close menu on click outside React.useEffect(() => { if (!menuOpen) return; const onDocClick = (e) => { if (menuRef.current && !menuRef.current.contains(e.target)) setMenuOpen(false); }; const onEsc = (e) => { if (e.key === 'Escape') setMenuOpen(false); }; document.addEventListener('mousedown', onDocClick); document.addEventListener('keydown', onEsc); return () => { document.removeEventListener('mousedown', onDocClick); document.removeEventListener('keydown', onEsc); }; }, [menuOpen]); const isCustom = !!d.status; // shared domains have no `status` field return (
{ setHovered(true); if (!current) e.currentTarget.style.background = '#161616'; }} onMouseLeave={(e) => { setHovered(false); if (!current) e.currentTarget.style.background = 'transparent'; }} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '9px 14px', cursor: 'pointer', borderLeft: `2px solid ${current ? '#10B981' : 'transparent'}`, background: current ? '#181818' : 'transparent', transition: 'background 100ms', position: 'relative', }} >
{d.premium ? : null}
@{d.domain} {d.status === 'pending' && } {d.badges?.map((b) => )} {/* Kebab menu — only for custom domains the user controls */} {isCustom && (
{menuOpen && (
e.stopPropagation()} style={{ position: 'absolute', top: '100%', right: 0, marginTop: 4, zIndex: 60, background: '#0f0f0f', border: '1px solid #262626', borderRadius: 6, boxShadow: '0 8px 24px rgba(0,0,0,0.6)', minWidth: 180, overflow: 'hidden', animation: 'slideDown 120ms ease', }} > { onShowDns && onShowDns(); setMenuOpen(false); }}>DNS records { onReverify && onReverify(); setMenuOpen(false); }}>Re-verify
{ if (confirm(`Remove @${d.domain}? Mail to this domain will stop being received.`)) { onRemove && onRemove(); } setMenuOpen(false); }} > Remove domain
)}
)}
); }; const DomainMenuItem = ({ children, onClick, danger }) => ( ); // // SHOW DNS RECORDS MODAL — re-open the DNS records for an existing custom domain // at any time. Reuses the same DnsRow component as the add flow. // const ShowDnsRecordsModal = ({ open, onClose, domain, records }) => { if (!open || !records) return null; return (

Each chip maps to a separate field in the registrar's UI (Type / Host / Mail Server / Priority).

VERIFY WITH
dig {domain} MX +short dig _tempmail.{domain} TXT +short
); }; // // ADD DOMAIN MODAL (multi-step) // const AddDomainModal = ({ open, onClose, onAdd }) => { const [step, setStep] = React.useState(1); const [domain, setDomain] = React.useState(''); const [verifying, setVerifying] = React.useState(false); const [verifyResult, setVerifyResult] = React.useState(null); // null | 'success' | { missing: string[] } const [registering, setRegistering] = React.useState(false); const [error, setError] = React.useState(''); const [domainId, setDomainId] = React.useState(null); const [records, setRecords] = React.useState(null); // { mx: {host, value}, txt: {host, value} } React.useEffect(() => { if (open) { setStep(1); setDomain(''); setVerifyResult(null); setError(''); setDomainId(null); setRecords(null); setVerifying(false); setRegistering(false); } }, [open]); if (!open) return null; const valid = /^[a-z0-9][a-z0-9-]*(\.[a-z0-9][a-z0-9-]*)+$/i.test(domain); const goToStep2 = async () => { if (!valid || registering) return; setRegistering(true); setError(''); try { const res = await tmApi.customer.addDomain(domain); setDomainId(res.id); setRecords(res.verificationRecords); setStep(2); } catch (e) { const code = e.code || 'http_error'; if (code === 'domain_in_use') setError('This domain is already registered (by you or another user).'); else if (code === 'rate_limited') setError('Too many domain additions. Try again later.'); else setError(e.message || 'Could not add domain.'); } finally { setRegistering(false); } }; const runVerify = async () => { if (!domainId || verifying) return; setVerifying(true); setError(''); try { const res = await tmApi.customer.verifyDomain(domainId); if (res.status === 'verified') { setVerifyResult('success'); setStep(3); } else { setVerifyResult({ missing: res.missing || [] }); } } catch (e) { setError(e.message || 'Verification check failed.'); } finally { setVerifying(false); } }; return ( {/* Stepper */}
{[1, 2, 3].map((n) => (
= n ? '#10B981' : '#262626', transition: 'background 200ms', }} /> ))}
{step === 1 && (
{ setDomain(e.target.value.toLowerCase().trim()); setError(''); }} onKeyDown={(e) => e.key === 'Enter' && valid && goToStep2()} placeholder="example.com" disabled={registering} style={{ width: '100%', background: '#0a0a0a', border: `1px solid ${(domain && !valid) || error ? '#DC2626' : '#262626'}`, color: '#EDEDED', fontFamily: "'JetBrains Mono', monospace", fontSize: 14, padding: '12px 14px', borderRadius: 6, outline: 'none', boxSizing: 'border-box' }} />

Enter the root domain you control. You'll add MX + TXT records next.

{error &&

{error}

}
Cancel {registering ? 'Registering…' : <>Continue }
)} {step === 2 && records && (

Add these two records at your DNS provider.

Namecheap, Cloudflare, Route 53 — all the same fields: Type, Host, Value{`, `}Priority (MX only).

{verifyResult && verifyResult !== 'success' && Array.isArray(verifyResult.missing) && verifyResult.missing.length > 0 && (
NOT YET PROPAGATED
{verifyResult.missing.map((m, i) => (
· {m}
))}
DNS typically propagates in 5–30 minutes. Try again shortly.
)} {error &&

{error}

}
{ onAdd(domain, 'pending'); onClose(); }}>Verify later {verifying ? 'Checking DNS…' : "I've added the records — Verify"}
)} {step === 3 && (

Domain verified

@{domain} is active.

Messages will start flowing in immediately.

{ onAdd(domain, 'verified'); onClose(); }}>Use this domain
)} ); }; // Color-coded label chips so each DNS field maps unambiguously to the // matching column in the registrar's form (Type / Host / Mail Server / // Priority on Namecheap, etc.). const DNS_LABEL_COLORS = { TYPE: { bg: '#3a0e0e', border: '#5a1a1a', color: '#F87171' }, // red HOST: { bg: '#3a2a08', border: '#5c3e08', color: '#F59E0B' }, // orange VALUE: { bg: '#2a3a08', border: '#3e5c08', color: '#FACC15' }, // yellow 'MAIL SERVER': { bg: '#2a3a08', border: '#3e5c08', color: '#FACC15' }, // yellow PRIORITY: { bg: '#0c2a4a', border: '#1e3f6b', color: '#60A5FA' }, // blue }; const DnsLabelChip = ({ label }) => { const c = DNS_LABEL_COLORS[label] || { bg: '#1a1a1a', border: '#262626', color: '#8A8A8A' }; return ( {label} ); }; const DnsRow = ({ type, host, value, priority, valueLabel, note }) => (
{priority && } {note &&
{note}
}
); const CopyableField = ({ label, value }) => { const [copied, setCopied] = React.useState(false); const copy = () => { navigator.clipboard?.writeText(value); setCopied(true); setTimeout(() => setCopied(false), 1200); }; return (
{value}
); }; // // MANAGE INBOXES — lists addresses the customer has ACTIVELY USED (received // at least one email). Lets them jump to an address or delete it. // const ManageInboxesModal = ({ open, onClose, onSelectAddress, currentAddress, toast }) => { const [inboxes, setInboxes] = React.useState(null); const [hoveredId, setHoveredId] = React.useState(null); const [menuOpenId, setMenuOpenId] = React.useState(null); const reload = () => { tmApi.customer.inboxes() .then((r) => setInboxes(r.inboxes)) .catch((e) => toast?.('Load failed: ' + e.message, 'error')); }; React.useEffect(() => { if (open) { reload(); } else { setInboxes(null); setMenuOpenId(null); setHoveredId(null); } }, [open]); React.useEffect(() => { if (!menuOpenId) return; const onDoc = (e) => { // close kebab menu when clicking anywhere outside if (!e.target.closest?.('[data-kebab-menu]')) setMenuOpenId(null); }; document.addEventListener('mousedown', onDoc); return () => document.removeEventListener('mousedown', onDoc); }, [menuOpenId]); if (!open) return null; const remove = async (i) => { setMenuOpenId(null); if (!confirm(`Delete ${i.address} and all ${i.emailCount} email${i.emailCount === 1 ? '' : 's'}? This cannot be undone.`)) return; try { await tmApi.customer.removeInbox(i.id); toast?.(`Removed ${i.address}`); reload(); } catch (e) { toast?.('Delete failed: ' + e.message, 'error'); } }; const switchTo = (i) => { onSelectAddress?.(i.address); onClose(); }; return ( {!inboxes ? (
LOADING…
) : inboxes.length === 0 ? (
▒ NO MAIL YET ▒

You haven't received any mail on any address yet.

Pick an address using N and have something send mail to it — it'll show up here.

) : ( {inboxes.map((i) => { const isCurrent = i.address === currentAddress; return ( setHoveredId(i.id)} onMouseLeave={() => setHoveredId(null)} onClick={() => switchTo(i)} style={{ borderBottom: '1px solid #151515', cursor: 'pointer', background: isCurrent ? '#0f1f1a' : (hoveredId === i.id ? '#101010' : 'transparent'), transition: 'background 100ms', }} > ); })}
EMAIL DOMAIN EMAILS
{isCurrent && } {i.address.split('@')[0]}
@{i.domain} {i.emailCount} e.stopPropagation()} data-kebab-menu > {menuOpenId === i.id && (
{ setMenuOpenId(null); switchTo(i); }}> Open this inbox { navigator.clipboard?.writeText(i.address); toast?.('Address copied'); setMenuOpenId(null); }}> Copy address
remove(i)}> Delete inbox + emails
)}
)}
Inboxes only show here once they've received at least one email. Empty addresses you've typed are kept silently and won't appear in this list.
); }; const InboxMenuItem = ({ children, onClick, danger }) => ( ); const kbdStyle = { fontFamily: "'JetBrains Mono', monospace", fontSize: 10, padding: '1px 5px', border: '1px solid #262626', borderRadius: 3, background: '#0a0a0a', color: '#10B981', }; // // MANAGE DOMAINS // const ManageDomainsModal = ({ open, onClose, domains, onAdd, onRemove }) => { if (!open) return null; return (
{domains.custom.map((d) => ( ))}
DOMAIN STATUS ADDED EMAILS ACTIONS
@{d.domain} {d.status === 'verified' ? ( VERIFIED ) : ( PENDING )} {d.added} {d.count}
{d.status === 'pending' && Re-verify} DNS onRemove(d.id)}>Remove

Custom domains don't expire. Email retention is configurable per-domain.

Add domain
); }; const Th = ({ children, style }) => ( {children} ); const MiniBtn = ({ children, onClick, danger }) => ( ); // // SETTINGS DRAWER // const SettingsDrawer = ({ open, onClose, settings, setSettings, onShowShortcuts, onSignOut, codePreview }) => { if (!open) return null; return ( <>

Settings

setSettings({ ...settings, refresh: v })} options={[['off', 'Off'], ['10', '10s'], ['30', '30s'], ['60', '60s']]} /> setSettings({ ...settings, retention: v })} options={[['24', '24h'], ['72', '72h'], ['7', '7d'], ['30', '30d']]} /> setSettings({ ...settings, showCodes: v })} /> setSettings({ ...settings, sound: v })} />
setSettings({ ...settings, density: v })} options={[['compact', 'Compact'], ['cozy', 'Cozy'], ['roomy', 'Roomy']]} />
ACCESS CODE
{codePreview || '????-XXXX-XXXX-????'}
); }; const linkBtn = { background: 'none', border: 'none', color: '#8A8A8A', cursor: 'pointer', fontFamily: "'Geist', system-ui, sans-serif", fontSize: 12, display: 'flex', alignItems: 'center', gap: 4, }; const Section = ({ label, children }) => (
{label}
{children}
); const Row = ({ label, children }) => (
{label} {children}
); const SegControl = ({ value, onChange, options }) => (
{options.map(([v, label]) => ( ))}
); const Toggle = ({ value, onChange }) => ( ); // // SHORTCUTS MODAL // const ShortcutsModal = ({ open, onClose }) => { if (!open) return null; const shortcuts = [ ['R', 'Refresh inbox'], ['N', 'Save / pin selected email'], ['D', 'Delete selected email'], ['J', 'Next email'], ['K', 'Previous email'], ['/', 'Focus address input'], ['C', 'Copy current address'], ['G', 'Open domain selector'], ['?', 'Show this help'], ['Esc', 'Close modal / dropdown'], ]; return (
{shortcuts.map(([k, desc]) => (
{desc} {k}
))}
); }; // // QR MODAL // const QRModal = ({ open, onClose, address }) => { if (!open) return null; // Generate fake QR pattern const grid = React.useMemo(() => { const seed = address.split('').reduce((a, c) => a + c.charCodeAt(0), 0); const arr = []; for (let i = 0; i < 25; i++) { const row = []; for (let j = 0; j < 25; j++) { row.push((seed * (i + 1) * (j + 1) + i * j) % 3 === 0); } arr.push(row); } // finder patterns const setFinder = (r, c) => { for (let i = 0; i < 7; i++) for (let j = 0; j < 7; j++) { const edge = i === 0 || i === 6 || j === 0 || j === 6; const inner = i >= 2 && i <= 4 && j >= 2 && j <= 4; arr[r + i][c + j] = edge || inner; } }; setFinder(0, 0); setFinder(0, 18); setFinder(18, 0); return arr; }, [address]); return (
{grid.flat().map((on, i) => (
))}
{address}
); }; // // GENERIC MODAL SHELL // const ModalShell = ({ onClose, title, subtitle, children, wide }) => ( <>

{title}

{subtitle &&

{subtitle}

}
{children}
); const PrimaryBtn = ({ children, onClick, disabled }) => ( ); const MutedBtn = ({ children, onClick }) => ( ); Object.assign(window, { DomainDropdown, AddDomainModal, ManageDomainsModal, ManageInboxesModal, SettingsDrawer, ShortcutsModal, QRModal, ShowDnsRecordsModal, Badge, PrimaryBtn, MutedBtn, ModalShell, });