// 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 (
);
}
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 && (
)}
);
})}
);
}
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;