Files
drover-go/docs/design/v2/drover-studio.jsx
T
root 113616b039
Release / release (push) Successful in 3m19s
docs/design/v2: add 12-variant React design archive
Stash the full claude.ai/design output (12 JSX variants — brutalist,
classic, cli, compact, fluent-live, glass, hero-live, minimal,
sketches, studio, wizard-live, workshop — plus shared hooks and a
standalone HTML preview) for reference when we get to the Wails
frontend in Phase 6/7.

Source archive: C:\Users\root\Downloads\app(1).zip (~1MB).

Not wired into any build target yet — current GUI is the temporary
MessageBox stub. Pulling these in is the goal of the Wails phase.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 03:12:02 +03:00

310 lines
15 KiB
React

// 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 (
<div style={{
width: 480, height: 640, background: t.bg, color: t.text, display:'flex', flexDirection:'column',
fontFamily: fontUI, fontSize: 13, overflow:'hidden', border:'1px solid #000',
}}>
<StdTitle t={t}/>
<div style={{ flex: 1, overflow:'auto', padding:'14px 16px 4px' }}>
<StdSection t={t} title="SOCKS5 Proxy">
<div style={{ display:'flex', gap: 10 }}>
<StdField t={t} label="Host" style={{ flex: 1 }}>
<StdInput t={t} fontUI={fontUI} value={D.form.host}
onChange={v => D.update({ host: v })} onSubmit={D.runCheck}
placeholder="95.165.72.59 или example.com"/>
</StdField>
<StdField t={t} label="Port" style={{ width: 96 }}>
<StdInput t={t} fontUI={fontUI} value={D.form.port}
onChange={v => D.update({ port: v.replace(/\D/g,'') })} onSubmit={D.runCheck}
placeholder="12334" inputMode="numeric"/>
</StdField>
</div>
<div style={{ height: 12 }}/>
<StdCheck t={t} checked={D.form.auth}
onChange={(v) => { D.update({ auth: v }); if (v) setTimeout(()=>document.getElementById('std-login')?.focus(),30); }}>
Authentication
</StdCheck>
<div style={{
display:'flex', gap: 10, marginTop: 10,
opacity: D.form.auth ? 1 : 0.4, pointerEvents: D.form.auth?'auto':'none',
}}>
<StdField t={t} label="Login" style={{ flex: 1 }}>
<StdInput id="std-login" t={t} fontUI={fontUI} value={D.form.login}
onChange={v => D.update({ login: v })} onSubmit={D.runCheck} placeholder="user" disabled={!D.form.auth}/>
</StdField>
<StdField t={t} label="Password" style={{ flex: 1 }}>
<StdInput t={t} fontUI={fontUI} value={D.form.password} type="password"
onChange={v => D.update({ password: v })} onSubmit={D.runCheck} placeholder="••••••"
disabled={!D.form.auth}/>
</StdField>
</div>
<div style={{ height: 14 }}/>
<button onClick={D.runCheck} disabled={D.phase==='checking'||isActive} style={{
width:'100%', padding:'9px 14px', borderRadius: 6, border:'none',
background: (D.phase==='checking'||isActive) ? t.panel2 : t.accent,
color: (D.phase==='checking'||isActive) ? t.dimmer : t.primaryFg,
fontWeight: 600, fontSize: 13, cursor: D.phase==='checking'?'not-allowed':'pointer',
fontFamily: fontUI,
}}>{D.phase==='checking' ? 'Checking…' : 'Check connection'}</button>
</StdSection>
<div style={{ height: 14 }}/>
<StdSection t={t} title="Status" right={
D.phase==='checking' ? `${Object.keys(D.results).length}/${D.tests.length}` :
D.lastSummary ? (D.lastSummary.failed === 0 ? 'all passed' : `${D.lastSummary.failed} failed`) : null
}>
{D.phase === 'idle'
? <div style={{ display:'flex', alignItems:'center', gap: 8, color: t.dim, padding:'2px 0' }}>
<span style={{ width: 6, height: 6, borderRadius: 3, background: t.dimmer }}/>
Ready to check
</div>
: <StdStatus t={t} D={D} palette={palette} fontMono={fontMono}/>}
</StdSection>
<div style={{ height: 14 }}/>
<StdSection t={t}>
<div style={{ display:'flex', gap: 10 }}>
<StdStartBtn t={t} D={D} fontUI={fontUI}/>
<StdStopBtn t={t} D={D} fontUI={fontUI}/>
</div>
{isActive && (
<div style={{
marginTop: 12, paddingTop: 12, borderTop:`1px solid ${t.border}`,
display:'flex', justifyContent:'space-between', color: t.dim,
}}>
<StdStat icon={<window.IconArrowUp color={t.pass}/>} val={window.fmtBytes(D.stats.up)} fontMono={fontMono} t={t}/>
<StdStat icon={<window.IconArrowDown color={t.accent}/>} val={window.fmtBytes(D.stats.down)} fontMono={fontMono} t={t}/>
<StdStat val={D.stats.tcp} lbl="TCP" fontMono={fontMono} t={t}/>
<StdStat val={D.stats.udp} lbl="UDP" fontMono={fontMono} t={t}/>
<StdStat val={window.fmtUptime(D.stats.uptimeS)} lbl="UP" fontMono={fontMono} t={t}/>
</div>
)}
</StdSection>
<div style={{ height: 12 }}/>
</div>
<StdLogs t={t} D={D} fontMono={fontMono}/>
</div>
);
}
function StdTitle({ t }) {
const cell = { width: 44, height: 32, display:'flex', alignItems:'center', justifyContent:'center', cursor:'pointer', color: t.dim };
return (
<div style={{ height: 36, background: t.chrome, borderBottom:`1px solid ${t.border}`, display:'flex', alignItems:'center', userSelect:'none' }}>
<div style={{ display:'flex', alignItems:'center', gap: 9, padding:'0 14px', flex:1 }}>
<window.BrandMark size={15} color={t.accent}/>
<span style={{ fontSize: 13, fontWeight: 600 }}>Drover-Go</span>
<span style={{ fontSize: 11, color: t.dimmer }}>0.4.2</span>
</div>
<div style={{ display:'flex' }}>
<div style={cell}><window.IconGear color={t.dim}/></div>
<div style={cell}><window.IconMin color={t.dim}/></div>
<div style={cell}><window.IconClose color={t.dim}/></div>
</div>
</div>
);
}
function StdSection({ t, title, right, children }) {
return (
<section>
{(title || right) && (
<div style={{ display:'flex', alignItems:'baseline', marginBottom: 8 }}>
{title && <div style={{ fontSize: 11.5, fontWeight: 600, color: t.dim, letterSpacing: 0.5 }}>{title}</div>}
{right && <div style={{ marginLeft:'auto', fontSize: 11, color: t.dimmer, fontFamily:'"JetBrains Mono",monospace' }}>{right}</div>}
</div>
)}
<div style={{ background: t.panel, border:`1px solid ${t.border}`, borderRadius: 8, padding: 14 }}>
{children}
</div>
</section>
);
}
function StdField({ t, label, children, style }) {
return <label style={{ display:'flex', flexDirection:'column', gap: 5, ...style }}>
<span style={{ fontSize: 11.5, color: t.dim }}>{label}</span>{children}
</label>;
}
function StdInput({ t, fontUI, value, onChange, type, placeholder, onSubmit, disabled, id, inputMode }) {
return <input id={id} value={value} type={type||'text'} disabled={disabled}
inputMode={inputMode}
onChange={e => 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 (
<label style={{ display:'inline-flex', alignItems:'center', gap: 8, cursor:'pointer' }}>
<span style={{
width: 16, height: 16, borderRadius: 4, border:`1px solid ${checked?t.accent:t.border}`,
background: checked ? t.accent : 'transparent',
display:'flex', alignItems:'center', justifyContent:'center', transition:'all .12s',
}}>
{checked && <svg width="10" height="10" viewBox="0 0 10 10"><path d="M2 5l2 2 4-4.4" stroke={t.primaryFg} strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" fill="none"/></svg>}
</span>
<input type="checkbox" checked={checked} onChange={e => onChange(e.target.checked)} style={{display:'none'}}/>
<span style={{ fontSize: 13 }}>{children}</span>
</label>
);
}
function StdStatus({ t, D, palette, fontMono }) {
const allOk = D.lastSummary?.failed === 0;
return (
<div>
{D.phase === 'checking'
? <div style={{ display:'flex', alignItems:'center', gap: 8, marginBottom: 8 }}>
<window.StatusDot state="running" palette={palette} size={14}/>
<span style={{ fontWeight: 500 }}>Running diagnostics</span>
</div>
: <div style={{
padding:'7px 11px', marginBottom: 10, borderRadius: 6, fontSize: 12.5, fontWeight: 500,
background: allOk ? t.passSoft : t.warnSoft,
border:`1px solid ${allOk ? t.pass : t.warn}40`,
color: allOk ? t.pass : t.warn,
}}>
{allOk ? 'All checks passed. Ready to start.'
: `${D.lastSummary?.failed} of ${D.tests.length} checks failed. Some features won't work.`}
</div>}
<div>
{D.tests.map(test => {
const r = D.results[test.id];
const state = r?.result || (D.running === test.id ? 'running' : 'pending');
return (
<div key={test.id}>
<div style={{ display:'flex', alignItems:'center', gap: 10, height: 26 }}>
<window.StatusDot state={state} palette={palette} size={13}/>
<span style={{ color: state==='pending'?t.dim:t.text, fontSize: 13 }} title={test.desc}>{test.label}</span>
<span style={{
marginLeft:'auto', fontFamily: fontMono, fontSize: 11.5,
color: state==='failed'?t.danger:state==='skipped'?t.skip:t.dim,
}}>{r?.metric}</span>
{r?.result === 'failed' && (
<button onClick={() => D.toggleExpand(test.id)} style={{
background:'transparent', border:'none', cursor:'pointer', padding: 4, color: t.dim,
}}><window.IconChevron color={t.dim} dir={r.expanded?'up':'down'}/></button>
)}
</div>
{r?.result === 'failed' && r.expanded && (
<div className="drv-fadein" style={{
margin: '4px 0 6px 23px', padding: 10, borderRadius: 6,
background: t.danSoft, border: `1px solid ${t.danger}30`, fontSize: 12,
}}>
<div style={{ color: t.danger, fontWeight: 600, marginBottom: 3 }}>{r.error}</div>
<div style={{ color: t.dim, lineHeight: 1.5 }}>{r.hint}</div>
</div>
)}
</div>
);
})}
</div>
</div>
);
}
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 (
<div style={{
flex: 1, padding:'9px 14px', borderRadius: 6, fontWeight: 600, fontSize: 13,
background: warning ? t.warnSoft : t.passSoft, color: c, border:`1px solid ${c}55`,
display:'flex', alignItems:'center', justifyContent:'center', gap: 8,
}}>
<span className="drv-pulsedot" style={{ width: 7, height: 7, borderRadius: 4, background: c }}/>
Active{warning ? ' · UDP fallback' : ''}
</div>
);
}
return <button onClick={D.startProxy} disabled={!ok} style={{
flex: 1, padding:'9px 14px', borderRadius: 6, border:'none',
background: ok ? t.accent : t.panel2, color: ok ? t.primaryFg : t.dimmer,
fontWeight: 600, fontSize: 13, cursor: ok?'pointer':'not-allowed', fontFamily: fontUI,
}}>Start proxying</button>;
}
function StdStopBtn({ t, D, fontUI }) {
const enabled = D.phase === 'active';
return <button onClick={D.stopProxy} disabled={!enabled} style={{
flex: 1, padding:'9px 14px', borderRadius: 6, background:'transparent',
color: enabled ? t.text : t.dimmer, border:`1px solid ${t.border}`,
fontWeight: 600, fontSize: 13, cursor: enabled?'pointer':'not-allowed', fontFamily: fontUI,
}}>Stop</button>;
}
function StdStat({ icon, val, lbl, fontMono, t }) {
return <div style={{ display:'flex', alignItems:'center', gap: 4 }}>
{icon}<span style={{ fontFamily: fontMono, fontSize: 11.5, color: t.text }}>{val}</span>
{lbl && <span style={{ fontSize: 10, color: t.dimmer, textTransform:'uppercase', letterSpacing: 0.5 }}>{lbl}</span>}
</div>;
}
function StdLogs({ t, D, fontMono }) {
return (
<div style={{ borderTop:`1px solid ${t.border}`, background: t.chrome, flexShrink: 0 }}>
<button onClick={() => D.setLogsOpen(!D.logsOpen)} style={{
width:'100%', padding:'9px 16px', display:'flex', alignItems:'center', gap: 9,
background:'transparent', border:'none', color: t.dim, cursor:'pointer', fontSize: 12,
}}>
<window.IconChevron color={t.dim} dir={D.logsOpen?'down':'right'}/>
<span style={{ fontWeight: 600 }}>Logs</span>
<span style={{ marginLeft:'auto', fontFamily: fontMono, fontSize: 11, color: t.dimmer }}>{D.logs.length}</span>
</button>
{D.logsOpen && (
<>
<div style={{ display:'flex', gap: 6, padding:'0 16px 8px' }}>
{[['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]) => (
<button key={l} onClick={fn||undefined} style={{
background:'transparent', border:`1px solid ${t.border}`, borderRadius: 4,
padding:'4px 9px', fontSize: 11, color: t.dim, cursor:'pointer', fontFamily: fontMono,
}}>{l}</button>
))}
</div>
<div className="drv-log" ref={el => 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) => (
<div key={i}>
<span style={{ color: t.dimmer }}>{window.fmtTime(l.t)}</span>{' '}
<span style={{ color: l.level==='ERROR'?t.danger:l.level==='WARN'?t.warn:t.accent, fontWeight: 600 }}>[{l.level}]</span>{' '}
{l.msg}
</div>
))}
</div>
</>
)}
</div>
);
}
window.StudioWindow = StudioWindow;