// drover-workshop.jsx — Workshop: industrial slate + amber accent. Subtle texture.
const WSH = {
bg: '#181a1d', chrome: '#121417', panel: '#1f2226', panel2: '#23272c',
border: '#2c3036', borderSoft: '#222529',
text: '#e5e3df', dim: '#9a958c', dimmer: '#5e5b56',
accent: '#e89a3c', accentSoft: 'rgba(232,154,60,0.15)',
danger: '#d56654', warn: '#d4a248', pass: '#7fb37b', skip: '#7c7872',
inputBg: '#141619',
primaryFg: '#1a120a',
};
function WorkshopWindow({ initial }) {
const t = WSH;
const D = window.useDrover(initial);
const palette = { pending: t.dimmer, running: t.accent, passed: t.pass, failed: t.danger, skipped: t.skip };
const fontUI = '"IBM Plex Sans","Inter",system-ui,sans-serif';
const fontMono = '"IBM Plex Mono","JetBrains Mono",ui-monospace,monospace';
const isActive = D.phase === 'active';
return (
{/* subtle grain */}
{/* title — workshop label tag */}
Drover-Go
v0.4.2
{[
, , ].map((ic,i) => (
{ic}
))}
{/* form */}
SOCKS5 PROXY
D.update({ host: v })}
placeholder="95.165.72.59 / example.com" onSubmit={D.runCheck} fontUI={fontUI}/>
D.update({ port: v.replace(/\D/g,'') })}
placeholder="12334" inputMode="numeric" onSubmit={D.runCheck} fontUI={fontUI}/>
{ D.update({ auth: v }); if (v) setTimeout(()=>document.getElementById('wsh-login')?.focus(),30); }}>
AUTHENTICATION
D.update({ login: v })} placeholder="user" onSubmit={D.runCheck} fontUI={fontUI}/>
D.update({ password: v })} placeholder="••••••" onSubmit={D.runCheck} fontUI={fontUI}/>
{D.phase==='checking' ? 'Checking…' : 'Check connection'}
STATUS
{D.phase === 'idle'
?
Ready to check
:
}
);
}
function WshHead({ t, children, right }) {
return (
{children}
{right &&
{right}
}
);
}
function WshField({ t, label, children, style }) {
return
{label} {children}
;
}
function WshInput({ t, value, onChange, type, placeholder, onSubmit, disabled, id, fontUI, 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: 1, padding:'8px 10px',
fontSize: 13, fontFamily: fontUI, outline:'none', width:'100%', boxSizing:'border-box',
}}/>;
}
function WshSwitch({ t, checked, onChange, children }) {
return (
onChange(e.target.checked)} style={{ display:'none' }}/>
{children}
);
}
function WshStatus({ t, D, palette, fontMono }) {
return (
{D.phase === 'checking'
?
Running diagnostics…
:
{D.lastSummary?.failed === 0
? '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' && (
D.toggleExpand(test.id)} style={{
background:'transparent', border:'none', cursor:'pointer', padding: 4, color: t.dim,
}}>
)}
{r?.result === 'failed' && r.expanded && (
)}
);
})}
);
}
function WshStartBtn({ 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 warn' : ''}
);
}
return Start proxying ;
}
function WshStopBtn({ t, D, fontUI }) {
const enabled = D.phase === 'active';
return Stop ;
}
function Stat({ val, lbl, t, fontMono, hl }) {
return
{lbl}
{val}
;
}
function WshLogs({ t, D, fontMono }) {
return (
D.setLogsOpen(!D.logsOpen)} style={{
width:'100%', padding:'9px 16px', display:'flex', alignItems:'center', gap: 10,
background:'transparent', border:'none', color: t.dim, cursor:'pointer', fontSize: 11,
letterSpacing: 1.5, textTransform:'uppercase',
}}>
Logs
{D.logs.length} lines
{D.logsOpen && (
<>
{[['copy', () => navigator.clipboard?.writeText(D.logs.map(x=>`[${x.level}] ${x.msg}`).join('\n'))],
['clear', D.clearLogs], ['file', null]].map(([l, fn]) => (
{l}
))}
el && (el.scrollTop = el.scrollHeight)}
style={{ maxHeight: 130, overflowY:'auto', padding:'8px 16px',
fontFamily: fontMono, fontSize: 10.5, lineHeight: 1.6, color: t.dim, background: t.panel }}>
{D.logs.map((l,i) => (
{window.fmtTime(l.t)} {' '}
[{l.level}] {' '}
{l.msg}
))}
>
)}
);
}
window.WorkshopWindow = WorkshopWindow;