// drover-studio.jsx — Studio: modern, calm, Linear-ish. Subtle blue accent. const STD = { bg: '#0e0f12', chrome: '#0a0b0d', panel: '#15171b', panel2: '#1a1c21', border: '#22252b', borderSoft: '#1a1c21', text: '#e7e9ee', dim: '#9095a0', dimmer: '#5e6470', accent: '#6c8aff', accentSoft: 'rgba(108,138,255,0.14)', danger: '#ff7d70', danSoft: 'rgba(255,125,112,0.12)', warn: '#f5b13d', warnSoft: 'rgba(245,177,61,0.12)', pass: '#6cd391', passSoft: 'rgba(108,211,145,0.12)', skip: '#7e848e', inputBg: '#0e0f12', primaryFg: '#0c1226', }; function StudioWindow({ initial }) { const t = STD; const D = window.useDrover(initial); const palette = { pending: t.dimmer, running: t.accent, passed: t.pass, failed: t.danger, skipped: t.skip }; const fontUI = '"Inter","Segoe UI",system-ui,-apple-system,sans-serif'; const fontMono = '"JetBrains Mono",ui-monospace,monospace'; const isActive = D.phase === 'active'; return (
D.update({ host: v })} onSubmit={D.runCheck} placeholder="95.165.72.59 или example.com"/> D.update({ port: v.replace(/\D/g,'') })} onSubmit={D.runCheck} placeholder="12334" inputMode="numeric"/>
{ D.update({ auth: v }); if (v) setTimeout(()=>document.getElementById('std-login')?.focus(),30); }}> Authentication
D.update({ login: v })} onSubmit={D.runCheck} placeholder="user" disabled={!D.form.auth}/> D.update({ password: v })} onSubmit={D.runCheck} placeholder="••••••" disabled={!D.form.auth}/>
{D.phase === 'idle' ?
Ready to check
: }
{isActive && (
} val={window.fmtBytes(D.stats.up)} fontMono={fontMono} t={t}/> } val={window.fmtBytes(D.stats.down)} fontMono={fontMono} t={t}/>
)}
); } function StdTitle({ t }) { const cell = { width: 44, height: 32, display:'flex', alignItems:'center', justifyContent:'center', cursor:'pointer', color: t.dim }; return (
Drover-Go 0.4.2
); } function StdSection({ t, title, right, children }) { return (
{(title || right) && (
{title &&
{title}
} {right &&
{right}
}
)}
{children}
); } function StdField({ t, label, children, style }) { return ; } function StdInput({ t, fontUI, value, onChange, type, placeholder, onSubmit, disabled, id, inputMode }) { return onChange(e.target.value)} placeholder={placeholder} onKeyDown={e => e.key === 'Enter' && onSubmit?.()} style={{ background: t.inputBg, color: disabled ? t.dimmer : t.text, border:`1px solid ${t.border}`, borderRadius: 5, padding:'8px 10px', fontSize: 13, fontFamily: fontUI, outline:'none', width:'100%', boxSizing:'border-box', }}/>; } function StdCheck({ t, checked, onChange, children }) { return ( ); } function StdStatus({ t, D, palette, fontMono }) { const allOk = D.lastSummary?.failed === 0; return (
{D.phase === 'checking' ?
Running diagnostics…
:
{allOk ? 'All checks passed. Ready to start.' : `${D.lastSummary?.failed} of ${D.tests.length} checks failed. Some features won't work.`}
}
{D.tests.map(test => { const r = D.results[test.id]; const state = r?.result || (D.running === test.id ? 'running' : 'pending'); return (
{test.label} {r?.metric} {r?.result === 'failed' && ( )}
{r?.result === 'failed' && r.expanded && (
{r.error}
{r.hint}
)}
); })}
); } function StdStartBtn({ t, D, fontUI }) { 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 StdStopBtn({ t, D, fontUI }) { const enabled = D.phase === 'active'; return ; } function StdStat({ icon, val, lbl, fontMono, t }) { return
{icon}{val} {lbl && {lbl}}
; } function StdLogs({ t, D, fontMono }) { return (
{D.logsOpen && ( <>
{[['Copy all', () => navigator.clipboard?.writeText(D.logs.map(x=>`[${x.level}] ${x.msg}`).join('\n'))], ['Clear', D.clearLogs], ['Open log file', null]].map(([l, fn]) => ( ))}
el && (el.scrollTop = el.scrollHeight)} style={{ maxHeight: 130, overflowY:'auto', padding:'8px 16px', fontFamily: fontMono, fontSize: 11, lineHeight: 1.6, color: t.dim, background: t.panel }}> {D.logs.map((l,i) => (
{window.fmtTime(l.t)}{' '} [{l.level}]{' '} {l.msg}
))}
)}
); } window.StudioWindow = StudioWindow;