// drover-v2.jsx — Round 2: four new dark-only variants.
// Reuses window.useDrover / StatusDot / icons / fmt helpers from drover-shared.jsx.
// =============================================================================
// V5 — COMPACT PRO. High-density, table-like. Wireshark/HTOP feel. Mono numbers.
// =============================================================================
const CPT = {
bg: '#101216', chrome: '#0a0c0f', panel: '#15181d', panel2: '#1a1d23',
border: '#262a31', borderSoft: '#1d2026',
text: '#e1e3e8', dim: '#838892', dimmer: '#535862',
accent: '#7aa9ff', danger: '#e5685a', warn: '#d6a64a', pass: '#5fc888', skip: '#6a6f78',
inputBg: '#0c0e12', primaryFg: '#0b1426',
};
function CompactWindow({ initial }) {
const t = CPT;
const D = window.useDrover(initial);
const palette = { pending: t.dimmer, running: t.accent, passed: t.pass, failed: t.danger, skipped: t.skip };
const fontMono = '"JetBrains Mono","SF Mono",ui-monospace,Consolas,monospace';
const fontUI = '"Inter","Segoe UI",system-ui,sans-serif';
const isActive = D.phase === 'active';
return (
{/* title bar */}
drover-go
0.4.2
· {D.form.host}:{D.form.port}{D.form.auth ? ' · auth' : ''}
{/* Row 1: form, single tight line */}
SOCKS5
D.update({ host: v })} placeholder="host" style={{ flex: 1 }}
onSubmit={D.runCheck}/>
:
D.update({ port: v.replace(/\D/g,'') })}
placeholder="port" style={{ width: 64 }} onSubmit={D.runCheck}/>
{
D.update({ auth: v }); if (v) setTimeout(()=>document.getElementById('cpt-login')?.focus(),30);
}}>auth
{D.form.auth && (
D.update({ login: v })} placeholder="login"
style={{ flex: 1 }} onSubmit={D.runCheck}/>
D.update({ password: v })} placeholder="password"
style={{ flex: 1 }} onSubmit={D.runCheck}/>
)}
{/* Row 2: status table, full width */}
STATUS
{D.phase === 'idle' && 'idle'}
{D.phase === 'checking' && `${Object.keys(D.results).length}/${D.tests.length}`}
{(D.phase === 'checked' || D.phase === 'active') &&
(D.lastSummary?.failed === 0 ? 'all-pass' : `${D.lastSummary?.failed}/${D.tests.length} fail`)}
{D.phase === 'idle'
?
› ready to check_
:
}
{/* Row 3: actions */}
{isActive && (
{window.fmtBytes(D.stats.up)}
{window.fmtBytes(D.stats.down)}
tcp:{D.stats.tcp}
udp:{D.stats.udp}
up:{window.fmtUptime(D.stats.uptimeS)}
)}
{/* Logs */}
);
}
function CompactCell({ children, t, hover }) {
const [h, setH] = React.useState(false);
return setH(true)} onMouseLeave={()=>setH(false)} style={{
width: 32, height: 28, display:'flex', alignItems:'center', justifyContent:'center',
cursor:'pointer', background: h && hover ? hover : 'transparent', transition:'background .1s',
}}>{children}
;
}
function cptHead(t) {
return {
fontSize: 9.5, letterSpacing: 1.5, color: t.dim, fontWeight: 700,
marginBottom: 6, display:'flex', alignItems:'center',
};
}
function CptInput({ value, onChange, placeholder, type, style, onSubmit, t, fontMono, id }) {
return onChange(e.target.value)} placeholder={placeholder}
onKeyDown={e => e.key === 'Enter' && onSubmit?.()}
style={{
background: t.inputBg, color: t.text, border:`1px solid ${t.border}`,
borderRadius: 2, padding:'5px 7px', fontFamily: fontMono, fontSize: 11.5,
outline:'none', boxSizing:'border-box', ...style,
}}/>;
}
function CptCheck({ checked, onChange, t, children }) {
return (
);
}
function CompactStatusTable({ t, D, palette, fontMono }) {
return (
{D.tests.map((test) => {
const r = D.results[test.id];
const state = r?.result || (D.running === test.id ? 'running' : 'pending');
return (
{
state==='passed'?'✓':state==='failed'?'✗':state==='skipped'?'–':state==='running'?'›':'·'
}
{test.label}
{r?.metric || (state==='running'?'…':'')}
{r?.result === 'failed' && (
)}
{r?.result === 'failed' && r.expanded && (
)}
);
})}
);
}
function CompactStartBtn({ t, D, fontMono }) {
const allFailed = D.lastSummary && D.lastSummary.failed === D.tests.length;
const ok = D.phase === 'checked' && !allFailed;
const active = D.phase === 'active';
const warning = active && (D.lastSummary?.failed || 0) > 0;
if (active) {
const c = warning ? t.warn : t.pass;
return (
ACTIVE{warning ? ' · UDP-FALLBACK' : ''}
);
}
return (
);
}
function CompactStopBtn({ t, D, fontMono }) {
const enabled = D.phase === 'active';
return (
);
}
function CompactLogs({ t, D, fontMono }) {
return (
{D.logsOpen && (
<>
{[['copy', () => navigator.clipboard?.writeText(D.logs.map(x=>`[${x.level}] ${x.msg}`).join('\n'))],
['clear', D.clearLogs], ['file', null]].map(([l, fn]) => (
))}
el && (el.scrollTop = el.scrollHeight)}
style={{ maxHeight: 110, overflowY:'auto', padding:'5px 12px',
fontFamily: fontMono, fontSize: 10, lineHeight: 1.55, color: t.dim, background: t.panel }}>
{D.logs.map((l,i) => (
{window.fmtTime(l.t)}{' '}
{l.level.padEnd(5)}{' '}
{l.msg}
))}
>
)}
);
}
window.CompactWindow = CompactWindow;