113616b039
Release / release (push) Successful in 3m19s
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>
904 lines
63 KiB
React
904 lines
63 KiB
React
// Drover-Go · style sketches v2 — "feels like real software" set.
|
||
// Each artboard explores a DIFFERENT software UI paradigm + element vocabulary,
|
||
// not just a different palette. 480×640 fixed Win11 window unless noted.
|
||
|
||
const W = 480;
|
||
const H = 640;
|
||
|
||
const Win = ({ children, bg = '#fff', radius = 8, ring = 'rgba(0,0,0,.18)' }) => (
|
||
<div style={{
|
||
width: W, height: H, background: bg, borderRadius: radius,
|
||
boxShadow: `0 0 0 1px ${ring}, 0 24px 60px rgba(0,0,0,.18)`,
|
||
overflow: 'hidden', position: 'relative', display: 'flex', flexDirection: 'column'
|
||
}}>{children}</div>
|
||
);
|
||
|
||
const Mark = ({ size = 14, color = '#0c8c7a' }) => (
|
||
<svg width={size} height={size} viewBox="0 0 16 16" fill="none">
|
||
<circle cx="8" cy="8" r="6.5" stroke={color} strokeWidth="1.5"/>
|
||
<path d="M5.5 5.5 8.5 8 5.5 10.5" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
|
||
<path d="M9 5.5 12 8 9 10.5" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" opacity=".5"/>
|
||
</svg>
|
||
);
|
||
|
||
const TitleButtons = ({ color = '#666' }) => (
|
||
<div style={{ marginLeft: 'auto', display: 'flex', gap: 4, color }}>
|
||
<span style={{ width: 22, height: 22, display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>⚙</span>
|
||
<span style={{ width: 22, height: 22, display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>—</span>
|
||
<span style={{ width: 22, height: 22, display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>×</span>
|
||
</div>
|
||
);
|
||
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
// 1 · Fluent / Win11 native — the safe baseline
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
const SketchFluent = () => {
|
||
const f = "'Segoe UI Variable','Segoe UI',system-ui,sans-serif";
|
||
return (
|
||
<Win bg="#f3f3f3" radius={10}>
|
||
<div style={{ height: 36, display: 'flex', alignItems: 'center', padding: '0 14px', fontFamily: f, fontSize: 12.5, color: '#1a1a1a' }}>
|
||
<Mark color="#0067c0" />
|
||
<span style={{ marginLeft: 10, fontWeight: 600 }}>Drover-Go</span>
|
||
<span style={{ marginLeft: 8, color: '#888', fontSize: 11 }}>0.4.2</span>
|
||
<TitleButtons />
|
||
</div>
|
||
<div style={{ padding: 14, fontFamily: f, fontSize: 13.5, flex: 1, display: 'flex', flexDirection: 'column', gap: 10, overflow: 'hidden' }}>
|
||
<div style={{ background: '#fff', borderRadius: 8, padding: 12, boxShadow: '0 0 0 1px rgba(0,0,0,.05), 0 1px 3px rgba(0,0,0,.04)' }}>
|
||
<div style={{ fontSize: 12, fontWeight: 600, color: '#444', marginBottom: 10 }}>SOCKS5 Proxy</div>
|
||
<div style={{ display: 'flex', gap: 8 }}>
|
||
<FluentField flex={1} label="Host" value="95.165.72.59" />
|
||
<FluentField width={88} label="Port" value="12334" />
|
||
</div>
|
||
<label style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 10, fontSize: 12.5 }}>
|
||
<span style={{ width: 16, height: 16, borderRadius: 4, border: '1px solid #ccc', background: '#fff' }} />
|
||
<span style={{ color: '#888' }}>Authentication</span>
|
||
</label>
|
||
<button style={{ marginTop: 12, width: '100%', height: 34, background: '#0067c0', color: '#fff', border: 'none', borderRadius: 4, fontFamily: f, fontWeight: 600, fontSize: 13, boxShadow: '0 1px 0 rgba(0,0,0,.1) inset, 0 1px 2px rgba(0,103,192,.3)' }}>Check connection</button>
|
||
</div>
|
||
<div style={{ background: '#fff', borderRadius: 8, padding: 12, boxShadow: '0 0 0 1px rgba(0,0,0,.05), 0 1px 3px rgba(0,0,0,.04)', flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column' }}>
|
||
<div style={{ fontSize: 12, fontWeight: 600, color: '#444', marginBottom: 10 }}>Status</div>
|
||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, color: '#0067c0', fontSize: 12.5, marginBottom: 8 }}>
|
||
<span style={{ width: 14, height: 14, borderRadius: '50%', border: '2px solid #0067c0', borderRightColor: 'transparent', animation: 'spin 1s linear infinite' }} />
|
||
Running diagnostics…
|
||
</div>
|
||
{[
|
||
['✓', 'TCP reachability', '12 ms', '#107c10'],
|
||
['✓', 'SOCKS5 greeting', 'ok', '#107c10'],
|
||
['◐', 'TCP CONNECT to Discord', '…', '#0067c0'],
|
||
['·', 'UDP ASSOCIATE', '', '#bbb'],
|
||
['·', 'UDP round-trip via STUN', '', '#bbb'],
|
||
['·', 'Discord API reachable', '', '#bbb'],
|
||
].map(([ic, name, val, c], i) => (
|
||
<div key={i} style={{ display: 'flex', alignItems: 'center', padding: '4px 0', fontSize: 12.5 }}>
|
||
<span style={{ width: 18, color: c, fontWeight: 600 }}>{ic}</span>
|
||
<span style={{ flex: 1 }}>{name}</span>
|
||
<span style={{ color: '#888', fontFamily: "'Cascadia Mono', monospace", fontSize: 11.5 }}>{val}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<div style={{ display: 'flex', gap: 8 }}>
|
||
<button style={{ flex: 1, height: 36, background: '#0067c0', color: '#fff', border: 'none', borderRadius: 4, fontWeight: 600, opacity: 0.4 }}>Start proxying</button>
|
||
<button style={{ flex: 1, height: 36, background: '#fff', border: '1px solid #ddd', borderRadius: 4, color: '#aaa' }}>Stop</button>
|
||
</div>
|
||
</div>
|
||
<style>{`@keyframes spin{to{transform:rotate(360deg)}}`}</style>
|
||
</Win>
|
||
);
|
||
};
|
||
const FluentField = ({ label, value, flex, width }) => (
|
||
<label style={{ flex, width, display: 'block' }}>
|
||
<div style={{ fontSize: 11, color: '#888', marginBottom: 3 }}>{label}</div>
|
||
<div style={{ height: 30, background: '#fafafa', borderRadius: 4, padding: '0 10px', display: 'flex', alignItems: 'center', fontSize: 13, color: '#1a1a1a', borderBottom: '1.5px solid #888', boxShadow: '0 0 0 1px rgba(0,0,0,.06)' }}>{value}</div>
|
||
</label>
|
||
);
|
||
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
// 2 · Sidebar nav (VS Code / Postman / Tower) — left rail with sections
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
const SketchSidebar = () => {
|
||
const f = "'Inter', system-ui, sans-serif";
|
||
const m = "'JetBrains Mono', monospace";
|
||
return (
|
||
<Win bg="#1e1f22" radius={8} ring="rgba(255,255,255,.06)">
|
||
<div style={{ height: 30, display: 'flex', alignItems: 'center', padding: '0 12px', borderBottom: '1px solid #2a2c30', fontFamily: f, fontSize: 12, color: '#cbd1d9' }}>
|
||
<Mark color="#4f8eff" />
|
||
<span style={{ marginLeft: 8, fontWeight: 500 }}>Drover-Go</span>
|
||
<span style={{ marginLeft: 8, color: '#666' }}>— proxy: 95.165.72.59:12334</span>
|
||
<TitleButtons color="#888" />
|
||
</div>
|
||
<div style={{ flex: 1, display: 'flex', minHeight: 0 }}>
|
||
{/* sidebar */}
|
||
<div style={{ width: 56, background: '#191a1d', borderRight: '1px solid #2a2c30', display: 'flex', flexDirection: 'column', alignItems: 'center', padding: '8px 0', gap: 4, fontFamily: f }}>
|
||
{[['◉', 'Proxy', true], ['◇', 'Diagnose'], ['≋', 'Traffic'], ['≣', 'Logs'], ['⚙', 'Settings']].map(([ic, l, on], i) => (
|
||
<div key={i} style={{ width: 40, height: 40, borderRadius: 6, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', color: on ? '#fff' : '#888', background: on ? '#2c3140' : 'transparent', borderLeft: on ? '2px solid #4f8eff' : '2px solid transparent', position: 'relative', fontSize: 14 }}>
|
||
<span>{ic}</span>
|
||
<span style={{ fontSize: 8, marginTop: 1 }}>{l}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', minHeight: 0, fontFamily: f, fontSize: 13, color: '#cbd1d9' }}>
|
||
{/* breadcrumb / tabs */}
|
||
<div style={{ height: 30, display: 'flex', alignItems: 'flex-end', padding: '0 0 0 0', borderBottom: '1px solid #2a2c30', background: '#191a1d' }}>
|
||
<div style={{ padding: '6px 12px', background: '#1e1f22', borderRight: '1px solid #2a2c30', borderTop: '2px solid #4f8eff', fontSize: 11.5, color: '#fff' }}>● Proxy</div>
|
||
<div style={{ padding: '6px 12px', borderRight: '1px solid #2a2c30', fontSize: 11.5, color: '#888' }}>diagnostic.run</div>
|
||
</div>
|
||
<div style={{ flex: 1, padding: 14, display: 'flex', flexDirection: 'column', gap: 12, overflow: 'hidden' }}>
|
||
<div>
|
||
<div style={{ fontSize: 10.5, letterSpacing: '.1em', color: '#888', textTransform: 'uppercase', marginBottom: 8 }}>Endpoint</div>
|
||
<div style={{ display: 'flex', gap: 6, alignItems: 'stretch' }}>
|
||
<div style={{ background: '#2c3140', border: '1px solid #3a4050', borderRight: 'none', padding: '0 10px', fontFamily: m, fontSize: 11, color: '#4f8eff', display: 'flex', alignItems: 'center', borderRadius: '4px 0 0 4px' }}>SOCKS5</div>
|
||
<div style={{ flex: 1, background: '#15171b', border: '1px solid #3a4050', padding: '0 10px', display: 'flex', alignItems: 'center', fontFamily: m, fontSize: 12, color: '#fff' }}>95.165.72.59</div>
|
||
<div style={{ width: 70, background: '#15171b', border: '1px solid #3a4050', borderLeft: 'none', padding: '0 10px', display: 'flex', alignItems: 'center', fontFamily: m, fontSize: 12, color: '#fff', borderRadius: '0 4px 4px 0' }}>:12334</div>
|
||
<button style={{ padding: '0 16px', background: '#4f8eff', color: '#fff', border: 'none', borderRadius: 4, fontWeight: 600, fontSize: 12.5 }}>Run ▸</button>
|
||
</div>
|
||
</div>
|
||
<div style={{ flex: 1, minHeight: 0 }}>
|
||
<div style={{ fontSize: 10.5, letterSpacing: '.1em', color: '#888', textTransform: 'uppercase', marginBottom: 8 }}>Diagnostic results</div>
|
||
<div style={{ background: '#15171b', border: '1px solid #2a2c30', borderRadius: 5, fontFamily: m, fontSize: 11.5 }}>
|
||
{[
|
||
['✓', 'tcp_reachability', '12 ms', '#21d07a'],
|
||
['✓', 'socks5_greeting', 'ok', '#21d07a'],
|
||
['✓', 'socks5_authentication', 'ok', '#21d07a'],
|
||
['✓', 'tcp_connect_discord', '38 ms', '#21d07a'],
|
||
['✗', 'udp_associate', 'EPERM: blocked', '#ff5e5e'],
|
||
['—', 'udp_stun_rtt', 'skipped', '#666'],
|
||
['—', 'discord_api', 'skipped', '#666'],
|
||
].map(([ic, n, v, c], i) => (
|
||
<div key={i} style={{ display: 'flex', padding: '5px 10px', borderBottom: i < 6 ? '1px solid #1f2126' : 'none' }}>
|
||
<span style={{ width: 16, color: c }}>{ic}</span>
|
||
<span style={{ flex: 1, color: c === '#666' ? '#666' : '#cbd1d9' }}>{n}</span>
|
||
<span style={{ color: c }}>{v}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<div style={{ marginTop: 10, padding: 8, background: '#3a1f1f', border: '1px solid #6e2a2a', borderRadius: 5, fontSize: 11.5, color: '#ff9e9e' }}>
|
||
⚠ 1 of 7 checks failed. UDP voice/screenshare won't work.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{/* status bar */}
|
||
<div style={{ height: 22, display: 'flex', alignItems: 'center', padding: '0 10px', background: '#15171b', borderTop: '1px solid #2a2c30', fontFamily: m, fontSize: 10.5, color: '#888' }}>
|
||
<span style={{ color: '#ff9e9e' }}>● 6/7</span>
|
||
<span style={{ marginLeft: 12 }}>idle</span>
|
||
<span style={{ marginLeft: 'auto' }}>UTF-8 · LF · drover.toml</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</Win>
|
||
);
|
||
};
|
||
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
// 3 · Step wizard / Tab progression — DigiCert / Tunnelblick connect-flow
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
const SketchWizard = () => {
|
||
const f = "'Inter', system-ui, sans-serif";
|
||
return (
|
||
<Win bg="#fafafa" radius={8} ring="rgba(0,0,0,.12)">
|
||
<div style={{ height: 32, display: 'flex', alignItems: 'center', padding: '0 12px', borderBottom: '1px solid #ececec', fontFamily: f, fontSize: 12, color: '#1a1a1a' }}>
|
||
<Mark color="#1e6fd9" />
|
||
<span style={{ marginLeft: 8, fontWeight: 600 }}>Drover-Go · Setup</span>
|
||
<TitleButtons />
|
||
</div>
|
||
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', fontFamily: f, color: '#1a1a1a' }}>
|
||
{/* stepper */}
|
||
<div style={{ display: 'flex', padding: '14px 18px', gap: 0, alignItems: 'center', borderBottom: '1px solid #ececec', background: '#fff' }}>
|
||
{[['1', 'Configure', true], ['2', 'Verify', true], ['3', 'Connect', false]].map(([n, l, done], i) => (
|
||
<React.Fragment key={i}>
|
||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||
<span style={{ width: 22, height: 22, borderRadius: '50%', background: i === 1 ? '#1e6fd9' : (done ? '#1e6fd9' : '#ddd'), color: '#fff', fontSize: 11, fontWeight: 600, display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
|
||
{done && i !== 1 ? '✓' : n}
|
||
</span>
|
||
<span style={{ fontSize: 12, fontWeight: i === 1 ? 600 : 400, color: i === 1 ? '#1a1a1a' : '#888' }}>{l}</span>
|
||
</div>
|
||
{i < 2 && <div style={{ flex: 1, height: 1, background: i === 0 ? '#1e6fd9' : '#ddd', margin: '0 10px' }} />}
|
||
</React.Fragment>
|
||
))}
|
||
</div>
|
||
<div style={{ flex: 1, padding: '20px 22px', display: 'flex', flexDirection: 'column', gap: 14, overflow: 'hidden' }}>
|
||
<div>
|
||
<h2 style={{ margin: 0, fontSize: 18, fontWeight: 600 }}>Verifying your proxy</h2>
|
||
<div style={{ fontSize: 12.5, color: '#666', marginTop: 4 }}>We're running 7 checks against <span style={{ fontFamily: "'JetBrains Mono',monospace", color: '#1a1a1a' }}>95.165.72.59:12334</span> to make sure Discord will work end-to-end.</div>
|
||
</div>
|
||
{/* progress + ring */}
|
||
<div style={{ display: 'flex', alignItems: 'center', gap: 16, padding: 14, background: '#fff', border: '1px solid #ececec', borderRadius: 8 }}>
|
||
<svg width="64" height="64" viewBox="0 0 64 64">
|
||
<circle cx="32" cy="32" r="26" fill="none" stroke="#eee" strokeWidth="6" />
|
||
<circle cx="32" cy="32" r="26" fill="none" stroke="#1e6fd9" strokeWidth="6" strokeLinecap="round" strokeDasharray="163.4" strokeDashoffset="70" transform="rotate(-90 32 32)" />
|
||
<text x="32" y="36" textAnchor="middle" fontSize="14" fontWeight="600" fill="#1a1a1a">4/7</text>
|
||
</svg>
|
||
<div style={{ flex: 1 }}>
|
||
<div style={{ fontWeight: 600, fontSize: 13.5 }}>Testing UDP relay…</div>
|
||
<div style={{ fontSize: 12, color: '#666', marginTop: 2 }}>This usually takes 5–15 seconds.</div>
|
||
</div>
|
||
</div>
|
||
<div style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 12.5 }}>
|
||
{[
|
||
['✓', 'TCP reachability', '12 ms', '#21a655'],
|
||
['✓', 'SOCKS5 greeting', 'ok', '#21a655'],
|
||
['✓', 'SOCKS5 authentication', 'ok', '#21a655'],
|
||
['✓', 'TCP CONNECT to Discord', '38 ms', '#21a655'],
|
||
['◐', 'UDP ASSOCIATE', 'running…', '#1e6fd9'],
|
||
['·', 'UDP round-trip via STUN', '', '#bbb'],
|
||
['·', 'Discord API reachable', '', '#bbb'],
|
||
].map(([ic, n, v, c], i) => (
|
||
<div key={i} style={{ display: 'flex', alignItems: 'center', padding: '5px 10px', background: i === 4 ? '#eef4fc' : 'transparent', borderRadius: 4 }}>
|
||
<span style={{ width: 18, color: c, fontWeight: 600 }}>{ic}</span>
|
||
<span style={{ flex: 1, color: c === '#bbb' ? '#999' : '#1a1a1a' }}>{n}</span>
|
||
<span style={{ color: c, fontFamily: "'JetBrains Mono',monospace", fontSize: 11.5 }}>{v}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
<div style={{ height: 50, display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: 8, padding: '0 18px', borderTop: '1px solid #ececec', background: '#fff' }}>
|
||
<button style={{ height: 30, padding: '0 14px', background: '#fff', border: '1px solid #ddd', borderRadius: 4, fontSize: 12.5 }}>Back</button>
|
||
<button style={{ height: 30, padding: '0 14px', background: '#1e6fd9', color: '#fff', border: 'none', borderRadius: 4, fontSize: 12.5, fontWeight: 600, opacity: 0.5 }}>Continue →</button>
|
||
</div>
|
||
</div>
|
||
</Win>
|
||
);
|
||
};
|
||
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
// 4 · Network monitor — gauges, sparklines, multi-pane (Wireshark / Activity)
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
const SketchMonitor = () => {
|
||
const f = "'Inter', system-ui, sans-serif";
|
||
const m = "'JetBrains Mono', monospace";
|
||
return (
|
||
<Win bg="#fcfcfc" radius={6} ring="rgba(0,0,0,.12)">
|
||
<div style={{ height: 30, display: 'flex', alignItems: 'center', padding: '0 12px', borderBottom: '1px solid #e6e6e6', fontFamily: f, fontSize: 12, background: '#f5f5f5' }}>
|
||
<Mark color="#0c8c7a" />
|
||
<span style={{ marginLeft: 8, fontWeight: 600 }}>Drover-Go</span>
|
||
<span style={{ marginLeft: 8, fontFamily: m, fontSize: 10, color: '#21a655' }}>● ACTIVE 4m 12s</span>
|
||
<TitleButtons />
|
||
</div>
|
||
<div style={{ flex: 1, padding: 12, display: 'flex', flexDirection: 'column', gap: 10, fontFamily: f, fontSize: 12, color: '#222', overflow: 'hidden' }}>
|
||
{/* compact endpoint */}
|
||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '8px 10px', background: '#fff', border: '1px solid #e6e6e6', borderRadius: 5 }}>
|
||
<span style={{ width: 8, height: 8, borderRadius: '50%', background: '#21a655' }} />
|
||
<span style={{ fontFamily: m, fontSize: 11.5 }}>95.165.72.59:12334</span>
|
||
<span style={{ marginLeft: 'auto', color: '#888', fontSize: 11 }}>SOCKS5 · auth</span>
|
||
<button style={{ background: 'transparent', border: '1px solid #ddd', borderRadius: 4, padding: '2px 8px', fontSize: 11 }}>Edit</button>
|
||
</div>
|
||
{/* meters */}
|
||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2,1fr)', gap: 8 }}>
|
||
<Gauge label="Upload" v="142" unit="KB/s" pct={28} c="#0c8c7a" />
|
||
<Gauge label="Download" v="2.8" unit="MB/s" pct={84} c="#1e6fd9" />
|
||
</div>
|
||
{/* sparkline panel */}
|
||
<div style={{ background: '#fff', border: '1px solid #e6e6e6', borderRadius: 5, padding: 10 }}>
|
||
<div style={{ display: 'flex', alignItems: 'baseline', marginBottom: 6 }}>
|
||
<span style={{ fontSize: 10.5, letterSpacing: '.06em', color: '#888', textTransform: 'uppercase' }}>Throughput · last 60s</span>
|
||
<span style={{ marginLeft: 'auto', fontFamily: m, fontSize: 11, color: '#222' }}>peak 3.4 MB/s</span>
|
||
</div>
|
||
<svg width="100%" height="60" viewBox="0 0 400 60" preserveAspectRatio="none">
|
||
<path d="M0 50 L20 45 L40 30 L60 35 L80 18 L100 25 L120 12 L140 22 L160 8 L180 18 L200 28 L220 15 L240 30 L260 20 L280 38 L300 25 L320 32 L340 18 L360 24 L380 14 L400 20 L400 60 L0 60Z" fill="#1e6fd9" opacity=".15" />
|
||
<path d="M0 50 L20 45 L40 30 L60 35 L80 18 L100 25 L120 12 L140 22 L160 8 L180 18 L200 28 L220 15 L240 30 L260 20 L280 38 L300 25 L320 32 L340 18 L360 24 L380 14 L400 20" fill="none" stroke="#1e6fd9" strokeWidth="1.5" />
|
||
</svg>
|
||
</div>
|
||
{/* connections table */}
|
||
<div style={{ flex: 1, background: '#fff', border: '1px solid #e6e6e6', borderRadius: 5, overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
|
||
<div style={{ display: 'flex', padding: '6px 10px', background: '#f5f5f5', borderBottom: '1px solid #e6e6e6', fontSize: 10.5, color: '#888', letterSpacing: '.06em', textTransform: 'uppercase' }}>
|
||
<span style={{ width: 50 }}>Proto</span>
|
||
<span style={{ flex: 1 }}>Endpoint</span>
|
||
<span style={{ width: 80, textAlign: 'right' }}>Up</span>
|
||
<span style={{ width: 80, textAlign: 'right' }}>Down</span>
|
||
</div>
|
||
{[
|
||
['UDP', 'voice-eu-rtc-1.discord', '142 KB/s', '2.4 MB/s', '#1e6fd9'],
|
||
['TCP', 'gateway.discord:443', '8 KB/s', '12 KB/s', '#0c8c7a'],
|
||
['TCP', 'cdn.discordapp:443', '0', '380 KB/s', '#0c8c7a'],
|
||
['TCP', 'media.discordapp:443', '0', '8 KB/s', '#0c8c7a'],
|
||
].map((r, i) => (
|
||
<div key={i} style={{ display: 'flex', padding: '5px 10px', fontFamily: m, fontSize: 11, borderBottom: i < 3 ? '1px solid #f0f0f0' : 'none' }}>
|
||
<span style={{ width: 50, color: r[4], fontWeight: 600 }}>{r[0]}</span>
|
||
<span style={{ flex: 1 }}>{r[1]}</span>
|
||
<span style={{ width: 80, textAlign: 'right', color: '#666' }}>{r[2]}</span>
|
||
<span style={{ width: 80, textAlign: 'right', color: '#666' }}>{r[3]}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<div style={{ display: 'flex', gap: 8 }}>
|
||
<button style={{ flex: 1, height: 32, background: '#21a655', color: '#fff', border: 'none', borderRadius: 4, fontWeight: 600, fontSize: 12.5 }}>● Active · 4m 12s</button>
|
||
<button style={{ flex: 1, height: 32, background: '#fff', border: '1px solid #ddd', borderRadius: 4, fontSize: 12.5 }}>Stop</button>
|
||
</div>
|
||
</div>
|
||
</Win>
|
||
);
|
||
};
|
||
const Gauge = ({ label, v, unit, pct, c }) => (
|
||
<div style={{ background: '#fff', border: '1px solid #e6e6e6', borderRadius: 5, padding: 10 }}>
|
||
<div style={{ display: 'flex', alignItems: 'baseline' }}>
|
||
<span style={{ fontSize: 10.5, letterSpacing: '.06em', color: '#888', textTransform: 'uppercase' }}>{label}</span>
|
||
</div>
|
||
<div style={{ display: 'flex', alignItems: 'baseline', gap: 4, marginTop: 4 }}>
|
||
<span style={{ fontSize: 22, fontWeight: 600, fontFamily: "'JetBrains Mono',monospace" }}>{v}</span>
|
||
<span style={{ fontSize: 11, color: '#888' }}>{unit}</span>
|
||
</div>
|
||
<div style={{ height: 4, background: '#f0f0f0', borderRadius: 2, marginTop: 6, overflow: 'hidden' }}>
|
||
<div style={{ width: pct + '%', height: '100%', background: c }} />
|
||
</div>
|
||
</div>
|
||
);
|
||
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
// 5 · Inspector / Properties — devtools-style key:value list
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
const SketchInspector = () => {
|
||
const f = "'Inter', system-ui, sans-serif";
|
||
const m = "'JetBrains Mono', monospace";
|
||
return (
|
||
<Win bg="#fff" radius={6} ring="rgba(0,0,0,.12)">
|
||
<div style={{ height: 28, display: 'flex', alignItems: 'center', padding: '0 12px', borderBottom: '1px solid #ececec', fontFamily: f, fontSize: 11.5 }}>
|
||
<Mark color="#1a1a1a" size={12} />
|
||
<span style={{ marginLeft: 8, fontWeight: 600 }}>Drover-Go</span>
|
||
<span style={{ marginLeft: 8, color: '#888', fontSize: 10.5 }}>0.4.2</span>
|
||
<TitleButtons />
|
||
</div>
|
||
{/* tabs */}
|
||
<div style={{ height: 28, display: 'flex', borderBottom: '1px solid #ececec', fontFamily: f, fontSize: 11.5, background: '#fafafa' }}>
|
||
{['Inspector', 'Network', 'Console', 'Settings'].map((t, i) => (
|
||
<div key={t} style={{ padding: '0 14px', display: 'flex', alignItems: 'center', borderBottom: i === 0 ? '2px solid #1a1a1a' : '2px solid transparent', color: i === 0 ? '#1a1a1a' : '#888', fontWeight: i === 0 ? 600 : 400 }}>{t}</div>
|
||
))}
|
||
</div>
|
||
<div style={{ flex: 1, fontFamily: m, fontSize: 11.5, color: '#1a1a1a', overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
|
||
<Group title="proxy" expanded>
|
||
<KV k="protocol" v="SOCKS5" t="enum" />
|
||
<KV k="host" v={<input style={{ all: 'unset', width: '100%', fontFamily: m, fontSize: 11.5, color: '#1a1a1a' }} defaultValue="95.165.72.59" />} editable />
|
||
<KV k="port" v="12334" t="int" editable />
|
||
<KV k="auth.enabled" v="true" t="bool" />
|
||
<KV k="auth.user" v="alice" t="str" editable />
|
||
<KV k="auth.pass" v="••••••••" t="str" editable />
|
||
</Group>
|
||
<Group title="diagnostic.last_run" expanded>
|
||
<KV k="started_at" v="14:32:01" t="time" />
|
||
<KV k="duration" v="3.2 s" t="dur" />
|
||
<KV k="passed" v="7" t="int" color="#21a655" />
|
||
<KV k="failed" v="0" t="int" />
|
||
<KV k="checks[0].name" v="tcp_reachability" />
|
||
<KV k="checks[0].rtt" v="12 ms" />
|
||
<KV k="checks[1].name" v="socks5_greeting" />
|
||
<KV k="checks[2].name" v="socks5_authentication" />
|
||
</Group>
|
||
<Group title="state" expanded>
|
||
<KV k="status" v="ready" t="enum" color="#21a655" />
|
||
<KV k="active" v="false" t="bool" />
|
||
</Group>
|
||
</div>
|
||
<div style={{ height: 32, display: 'flex', gap: 6, padding: '0 8px', alignItems: 'center', borderTop: '1px solid #ececec', background: '#fafafa' }}>
|
||
<button style={{ height: 22, padding: '0 10px', background: '#fff', border: '1px solid #ccc', borderRadius: 3, fontFamily: f, fontSize: 11 }}>Run diagnostic</button>
|
||
<button style={{ height: 22, padding: '0 10px', background: '#1a1a1a', color: '#fff', border: 'none', borderRadius: 3, fontFamily: f, fontSize: 11, fontWeight: 600 }}>▸ Start proxying</button>
|
||
<span style={{ marginLeft: 'auto', fontFamily: m, fontSize: 10.5, color: '#888' }}>14 props · saved</span>
|
||
</div>
|
||
</Win>
|
||
);
|
||
};
|
||
const Group = ({ title, expanded, children }) => (
|
||
<div>
|
||
<div style={{ padding: '4px 10px', background: '#f0f0f0', fontSize: 10.5, fontWeight: 600, color: '#444', display: 'flex', alignItems: 'center' }}>
|
||
<span style={{ marginRight: 4, fontSize: 9 }}>{expanded ? '▼' : '▶'}</span>{title}
|
||
</div>
|
||
<div>{children}</div>
|
||
</div>
|
||
);
|
||
const KV = ({ k, v, t, color, editable }) => (
|
||
<div style={{ display: 'flex', padding: '3px 10px', borderBottom: '1px solid #f5f5f5', alignItems: 'center', fontSize: 11 }}>
|
||
<span style={{ width: 150, color: '#888' }}>{k}</span>
|
||
<span style={{ flex: 1, color: color || '#1a1a1a', background: editable ? '#f9f9f9' : 'transparent', borderRadius: 2, padding: '1px 4px' }}>{v}</span>
|
||
{t && <span style={{ marginLeft: 8, fontSize: 9.5, color: '#bbb', textTransform: 'uppercase' }}>{t}</span>}
|
||
</div>
|
||
);
|
||
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
// 6 · Command palette + form — Raycast / Linear / cmd+k feel
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
const SketchPalette = () => {
|
||
const f = "'Inter', system-ui, sans-serif";
|
||
const m = "'JetBrains Mono', monospace";
|
||
return (
|
||
<Win bg="#0f1015" radius={14} ring="rgba(255,255,255,.08)">
|
||
<div style={{ height: 32, display: 'flex', alignItems: 'center', padding: '0 14px', fontFamily: f, fontSize: 12, color: '#cbd1d9' }}>
|
||
<Mark color="#a5b4ff" />
|
||
<span style={{ marginLeft: 8, fontWeight: 500 }}>Drover-Go</span>
|
||
<TitleButtons color="#666" />
|
||
</div>
|
||
<div style={{ flex: 1, padding: 14, fontFamily: f, color: '#e8e8ea', display: 'flex', flexDirection: 'column', gap: 12, overflow: 'hidden' }}>
|
||
{/* command bar */}
|
||
<div style={{ background: '#15171c', border: '1px solid #2a2d36', borderRadius: 9, padding: '10px 14px', display: 'flex', alignItems: 'center', gap: 10, boxShadow: '0 0 0 3px rgba(165,180,255,.08)' }}>
|
||
<span style={{ color: '#a5b4ff', fontSize: 14 }}>⌘</span>
|
||
<input style={{ all: 'unset', flex: 1, fontFamily: f, fontSize: 13.5, color: '#fff' }} placeholder="Search commands…" defaultValue="check con" />
|
||
<span style={{ fontFamily: m, fontSize: 10, color: '#666', padding: '2px 6px', background: '#1a1c22', border: '1px solid #2a2d36', borderRadius: 4 }}>esc</span>
|
||
</div>
|
||
{/* suggestion */}
|
||
<div style={{ background: '#15171c', borderRadius: 9, padding: 6 }}>
|
||
<div style={{ padding: '8px 10px', borderRadius: 6, background: '#23264a', display: 'flex', alignItems: 'center', gap: 10 }}>
|
||
<span style={{ width: 22, height: 22, borderRadius: 5, background: '#a5b4ff', color: '#0f1015', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', fontWeight: 700, fontSize: 12 }}>▸</span>
|
||
<span style={{ flex: 1, fontSize: 13 }}>Check connection</span>
|
||
<span style={{ fontFamily: m, fontSize: 10.5, color: '#a5b4ff' }}>↵</span>
|
||
</div>
|
||
<div style={{ padding: '8px 10px', display: 'flex', alignItems: 'center', gap: 10, color: '#888' }}>
|
||
<span style={{ width: 22, height: 22, borderRadius: 5, background: '#1a1c22', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', fontSize: 12 }}>●</span>
|
||
<span style={{ flex: 1, fontSize: 13 }}>Start proxying</span>
|
||
<span style={{ fontFamily: m, fontSize: 10.5 }}>⇧↵</span>
|
||
</div>
|
||
</div>
|
||
{/* Form below — proxy quick-edit */}
|
||
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 10, minHeight: 0 }}>
|
||
<div style={{ fontSize: 10.5, letterSpacing: '.1em', color: '#666', textTransform: 'uppercase' }}>Proxy</div>
|
||
<div style={{ display: 'flex', gap: 8 }}>
|
||
<PalField flex={1} label="Host" value="95.165.72.59" />
|
||
<PalField width={86} label="Port" value="12334" />
|
||
</div>
|
||
<div style={{ fontSize: 10.5, letterSpacing: '.1em', color: '#666', textTransform: 'uppercase', marginTop: 4 }}>Last run · 7/7 passed</div>
|
||
{[
|
||
['tcp_reachability', '12 ms'],
|
||
['socks5_greeting', 'ok'],
|
||
['udp_associate', 'relay 1.2.3.4'],
|
||
['udp_stun_rtt', '24 ms'],
|
||
['discord_api', '200'],
|
||
].map(([n, v], i) => (
|
||
<div key={i} style={{ display: 'flex', padding: '4px 10px', fontFamily: m, fontSize: 11.5, background: '#15171c', borderRadius: 5 }}>
|
||
<span style={{ color: '#21d07a', marginRight: 8 }}>✓</span>
|
||
<span style={{ flex: 1, color: '#cbd1d9' }}>{n}</span>
|
||
<span style={{ color: '#888' }}>{v}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '8px 0', fontFamily: m, fontSize: 10.5, color: '#666' }}>
|
||
<span><kbd style={{ background: '#1a1c22', border: '1px solid #2a2d36', padding: '1px 5px', borderRadius: 3, color: '#cbd1d9' }}>↵</kbd> run</span>
|
||
<span><kbd style={{ background: '#1a1c22', border: '1px solid #2a2d36', padding: '1px 5px', borderRadius: 3, color: '#cbd1d9' }}>⇧↵</kbd> start</span>
|
||
<span style={{ marginLeft: 'auto' }}>idle · ready</span>
|
||
</div>
|
||
</div>
|
||
</Win>
|
||
);
|
||
};
|
||
const PalField = ({ label, value, flex, width }) => (
|
||
<label style={{ flex, width }}>
|
||
<div style={{ fontSize: 10.5, color: '#888', marginBottom: 4 }}>{label}</div>
|
||
<div style={{ height: 32, background: '#15171c', border: '1px solid #2a2d36', borderRadius: 7, padding: '0 10px', display: 'flex', alignItems: 'center', fontSize: 13, color: '#fff', fontFamily: "'JetBrains Mono',monospace" }}>{value}</div>
|
||
</label>
|
||
);
|
||
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
// 7 · Big toggle / Hero — TunnelBear / NordVPN big-button connect
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
const SketchHero = () => {
|
||
const f = "'Inter', system-ui, sans-serif";
|
||
const m = "'JetBrains Mono', monospace";
|
||
return (
|
||
<Win bg="#0e1426" radius={14} ring="rgba(255,255,255,.06)">
|
||
<div style={{ height: 36, display: 'flex', alignItems: 'center', padding: '0 14px', fontFamily: f, fontSize: 12.5, color: '#cbd1d9' }}>
|
||
<Mark color="#5dd4b3" />
|
||
<span style={{ marginLeft: 10, fontWeight: 600 }}>Drover-Go</span>
|
||
<span style={{ marginLeft: 'auto', fontSize: 11, color: '#5dd4b3' }}>● connected</span>
|
||
<TitleButtons color="#666" />
|
||
</div>
|
||
<div style={{ flex: 1, padding: '20px 18px', fontFamily: f, color: '#fff', display: 'flex', flexDirection: 'column', gap: 16, alignItems: 'center', overflow: 'hidden' }}>
|
||
{/* Big circular button */}
|
||
<div style={{ width: 180, height: 180, borderRadius: '50%', background: 'radial-gradient(circle at 35% 30%, #5dd4b3 0%, #2da085 60%, #1a6e5b 100%)', boxShadow: '0 0 60px rgba(93,212,179,.4), 0 0 0 6px rgba(93,212,179,.1), 0 0 0 12px rgba(93,212,179,.05)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexDirection: 'column', position: 'relative', marginTop: 12 }}>
|
||
<div style={{ position: 'absolute', inset: -20, borderRadius: '50%', border: '1px solid rgba(93,212,179,.2)', animation: 'pulse 2s infinite' }} />
|
||
<span style={{ fontSize: 36 }}>⏻</span>
|
||
<span style={{ fontSize: 11, letterSpacing: '.2em', marginTop: 4, opacity: .9 }}>ACTIVE</span>
|
||
</div>
|
||
<div style={{ textAlign: 'center' }}>
|
||
<div style={{ fontSize: 22, fontWeight: 600 }}>Discord is protected</div>
|
||
<div style={{ fontSize: 12.5, color: '#7a8499', marginTop: 4, fontFamily: m }}>via 95.165.72.59 · 4m 12s</div>
|
||
</div>
|
||
{/* Stats row */}
|
||
<div style={{ display: 'flex', gap: 0, width: '100%', background: '#15192a', borderRadius: 10, padding: 12, justifyContent: 'space-around' }}>
|
||
{[
|
||
['↑', '142', 'KB/s'],
|
||
['↓', '2.8', 'MB/s'],
|
||
['◇', '14', 'tcp'],
|
||
['◈', '3', 'udp'],
|
||
].map(([k, v, u], i) => (
|
||
<div key={i} style={{ textAlign: 'center' }}>
|
||
<div style={{ fontSize: 11, color: '#5dd4b3' }}>{k}</div>
|
||
<div style={{ fontSize: 18, fontWeight: 600, fontFamily: m, marginTop: 2 }}>{v}</div>
|
||
<div style={{ fontSize: 9.5, color: '#7a8499', letterSpacing: '.06em', textTransform: 'uppercase' }}>{u}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<button style={{ width: '100%', height: 38, background: 'transparent', color: '#cbd1d9', border: '1px solid #2a3042', borderRadius: 9, fontWeight: 500, fontSize: 13 }}>Disconnect</button>
|
||
<div style={{ width: '100%', fontSize: 11, color: '#5a6178', display: 'flex', alignItems: 'center', gap: 6 }}>
|
||
<span style={{ fontSize: 10 }}>▸</span><span>Logs (3 new)</span>
|
||
</div>
|
||
</div>
|
||
<style>{`@keyframes pulse{0%,100%{transform:scale(1);opacity:.4}50%{transform:scale(1.08);opacity:0}}`}</style>
|
||
</Win>
|
||
);
|
||
};
|
||
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
// 8 · Toolbar + ribbon — old-school Office / Audacity feel
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
const SketchToolbar = () => {
|
||
const f = "'Segoe UI', system-ui, sans-serif";
|
||
const m = "'Consolas', 'JetBrains Mono', monospace";
|
||
return (
|
||
<Win bg="#ebebeb" radius={4} ring="rgba(0,0,0,.18)">
|
||
<div style={{ height: 26, display: 'flex', alignItems: 'center', padding: '0 8px', background: 'linear-gradient(180deg,#f5f5f5,#dcdcdc)', borderBottom: '1px solid #b8b8b8', fontFamily: f, fontSize: 11 }}>
|
||
<Mark color="#1a1a1a" size={12} />
|
||
<span style={{ marginLeft: 6, fontWeight: 600 }}>Drover-Go</span>
|
||
<span style={{ marginLeft: 8, color: '#666' }}>— [Connected]</span>
|
||
<TitleButtons />
|
||
</div>
|
||
{/* menu bar */}
|
||
<div style={{ height: 22, display: 'flex', padding: '0 6px', background: '#ebebeb', borderBottom: '1px solid #c8c8c8', fontFamily: f, fontSize: 11, alignItems: 'center', gap: 2 }}>
|
||
{['File', 'Edit', 'Run', 'View', 'Tools', 'Help'].map(m => (
|
||
<span key={m} style={{ padding: '2px 8px', borderRadius: 2 }}>{m}</span>
|
||
))}
|
||
</div>
|
||
{/* ribbon */}
|
||
<div style={{ display: 'flex', padding: '6px 8px', background: 'linear-gradient(180deg,#f5f5f5,#e8e8e8)', borderBottom: '1px solid #c8c8c8', gap: 4 }}>
|
||
<Tool icon="▶" label="Run" primary />
|
||
<Tool icon="●" label="Start" />
|
||
<Tool icon="■" label="Stop" disabled />
|
||
<div style={{ width: 1, background: '#c8c8c8', margin: '4px 6px' }} />
|
||
<Tool icon="⚙" label="Config" />
|
||
<Tool icon="⎘" label="Copy" />
|
||
<div style={{ width: 1, background: '#c8c8c8', margin: '4px 6px' }} />
|
||
<Tool icon="≣" label="Logs" />
|
||
</div>
|
||
<div style={{ flex: 1, padding: 10, display: 'flex', flexDirection: 'column', gap: 8, fontFamily: f, fontSize: 11.5, color: '#1a1a1a', overflow: 'hidden' }}>
|
||
<Fieldset label="SOCKS5 Proxy">
|
||
<div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
|
||
<span style={{ width: 40 }}>Host:</span>
|
||
<div style={{ flex: 1, height: 22, background: '#fff', border: '1px solid #888', boxShadow: '1px 1px 0 #ccc inset', padding: '0 6px', display: 'flex', alignItems: 'center', fontFamily: m, fontSize: 11.5 }}>95.165.72.59</div>
|
||
<span>Port:</span>
|
||
<div style={{ width: 70, height: 22, background: '#fff', border: '1px solid #888', boxShadow: '1px 1px 0 #ccc inset', padding: '0 6px', display: 'flex', alignItems: 'center', fontFamily: m, fontSize: 11.5 }}>12334</div>
|
||
</div>
|
||
<div style={{ display: 'flex', gap: 8, alignItems: 'center', marginTop: 6 }}>
|
||
<span style={{ width: 14, height: 14, border: '1px solid #888', background: '#fff', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', fontSize: 10 }}>✓</span>
|
||
<span>Authentication</span>
|
||
<span style={{ marginLeft: 12 }}>User:</span>
|
||
<div style={{ flex: 1, height: 22, background: '#fff', border: '1px solid #888', boxShadow: '1px 1px 0 #ccc inset', padding: '0 6px', display: 'flex', alignItems: 'center' }}>alice</div>
|
||
</div>
|
||
</Fieldset>
|
||
<Fieldset label="Diagnostics" flex>
|
||
<div style={{ background: '#fff', border: '1px solid #888', boxShadow: '1px 1px 0 #ccc inset', flex: 1, fontFamily: m, fontSize: 11, overflow: 'auto' }}>
|
||
{[
|
||
['✓', 'TCP reachability', 'PASS · 12 ms'],
|
||
['✓', 'SOCKS5 greeting', 'PASS · ok'],
|
||
['✓', 'SOCKS5 authentication', 'PASS · ok'],
|
||
['✓', 'TCP CONNECT to Discord', 'PASS · 38 ms'],
|
||
['✓', 'UDP ASSOCIATE', 'PASS · relay 1.2.3.4'],
|
||
['✓', 'UDP round-trip via STUN', 'PASS · 24 ms'],
|
||
['✓', 'Discord API reachable', 'PASS · 200'],
|
||
].map((r, i) => (
|
||
<div key={i} style={{ display: 'flex', padding: '3px 8px', borderBottom: i < 6 ? '1px dotted #ddd' : 'none' }}>
|
||
<span style={{ width: 14, color: '#107c10' }}>{r[0]}</span>
|
||
<span style={{ flex: 1 }}>{r[1]}</span>
|
||
<span style={{ color: '#666' }}>{r[2]}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</Fieldset>
|
||
</div>
|
||
{/* status bar */}
|
||
<div style={{ height: 22, display: 'flex', padding: '0 8px', background: '#dcdcdc', borderTop: '1px solid #b8b8b8', alignItems: 'center', fontSize: 10.5, fontFamily: f, gap: 12 }}>
|
||
<span>Ready</span>
|
||
<span>● 7/7</span>
|
||
<span>↑ 142 KB/s ↓ 2.8 MB/s</span>
|
||
<span style={{ marginLeft: 'auto', fontFamily: m }}>uptime 4m 12s</span>
|
||
</div>
|
||
</Win>
|
||
);
|
||
};
|
||
const Tool = ({ icon, label, primary, disabled }) => (
|
||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', padding: '2px 8px', borderRadius: 2, background: primary ? 'linear-gradient(180deg,#fff,#e0e0e0)' : 'transparent', border: primary ? '1px solid #888' : '1px solid transparent', minWidth: 44, opacity: disabled ? 0.4 : 1 }}>
|
||
<span style={{ fontSize: 16, color: primary ? '#107c10' : '#1a1a1a' }}>{icon}</span>
|
||
<span style={{ fontSize: 10, marginTop: 1 }}>{label}</span>
|
||
</div>
|
||
);
|
||
const Fieldset = ({ label, children, flex }) => (
|
||
<div style={{ border: '1px solid #b8b8b8', borderRadius: 2, padding: '10px 8px 8px', position: 'relative', background: '#f5f5f5', flex: flex ? 1 : undefined, minHeight: 0, display: 'flex', flexDirection: 'column' }}>
|
||
<span style={{ position: 'absolute', top: -8, left: 8, background: '#ebebeb', padding: '0 4px', fontSize: 10.5, fontWeight: 600 }}>{label}</span>
|
||
{children}
|
||
</div>
|
||
);
|
||
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
// 9 · Modern dev tool — Studio (Linear-ish, restrained dark)
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
const SketchStudio = () => {
|
||
const f = "'Inter', sans-serif";
|
||
return (
|
||
<Win bg="#0e0f12" radius={12} ring="rgba(255,255,255,.08)">
|
||
<div style={{ height: 36, display: 'flex', alignItems: 'center', padding: '0 14px', fontFamily: f, fontSize: 12.5, color: '#e8e8ea' }}>
|
||
<Mark color="#7c8aff" />
|
||
<span style={{ marginLeft: 10, fontWeight: 600 }}>Drover-Go</span>
|
||
<span style={{ marginLeft: 8, color: '#5a5d6a', fontSize: 11 }}>0.4.2</span>
|
||
<TitleButtons color="#777a86" />
|
||
</div>
|
||
<div style={{ padding: 14, fontFamily: f, color: '#e8e8ea', flex: 1, display: 'flex', flexDirection: 'column', gap: 12, overflow: 'hidden' }}>
|
||
<div>
|
||
<div style={{ fontSize: 11, fontWeight: 600, letterSpacing: '.04em', color: '#a1a3ad', marginBottom: 8, textTransform: 'uppercase' }}>SOCKS5 Proxy</div>
|
||
<div style={{ display: 'flex', gap: 8 }}>
|
||
<StField flex={1} label="Host" value="95.165.72.59" />
|
||
<StField width={86} label="Port" value="12334" />
|
||
</div>
|
||
<label style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 10, fontSize: 12.5, color: '#a1a3ad' }}>
|
||
<span style={{ width: 14, height: 14, borderRadius: 4, background: '#7c8aff', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontSize: 10 }}>✓</span>
|
||
Authentication
|
||
</label>
|
||
<button style={{ marginTop: 12, width: '100%', height: 34, background: '#7c8aff', color: '#0a0a0c', border: 'none', borderRadius: 7, fontWeight: 600, fontSize: 13 }}>Check connection</button>
|
||
</div>
|
||
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', minHeight: 0 }}>
|
||
<div style={{ fontSize: 11, fontWeight: 600, letterSpacing: '.04em', color: '#a1a3ad', marginBottom: 8, display: 'flex', textTransform: 'uppercase' }}>
|
||
Status <span style={{ marginLeft: 'auto', fontSize: 11, color: '#21d07a', fontWeight: 500, textTransform: 'none' }}>● All systems</span>
|
||
</div>
|
||
<div style={{ background: '#15171c', border: '1px solid #1f2228', borderRadius: 9, padding: 4, flex: 1 }}>
|
||
{[
|
||
['TCP reachability', '12 ms'],
|
||
['SOCKS5 greeting', 'ok'],
|
||
['SOCKS5 authentication', 'ok'],
|
||
['TCP CONNECT to Discord', '38 ms'],
|
||
['UDP ASSOCIATE', 'relay 1.2.3.4'],
|
||
['UDP round-trip via STUN', '24 ms'],
|
||
['Discord API reachable', '200'],
|
||
].map(([n, v], i) => (
|
||
<div key={i} style={{ display: 'flex', alignItems: 'center', padding: '6px 8px', borderRadius: 6, fontSize: 12.5 }}>
|
||
<span style={{ width: 14, height: 14, borderRadius: '50%', background: '#21d07a22', border: '1px solid #21d07a', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', color: '#21d07a', fontSize: 9, marginRight: 10 }}>✓</span>
|
||
<span style={{ flex: 1 }}>{n}</span>
|
||
<span style={{ color: '#777a86', fontFamily: "'Geist Mono',monospace", fontSize: 11.5 }}>{v}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
<div style={{ display: 'flex', gap: 8 }}>
|
||
<button style={{ flex: 1, height: 36, background: '#7c8aff', color: '#0a0a0c', border: 'none', borderRadius: 7, fontWeight: 600 }}>Start proxying</button>
|
||
<button style={{ flex: 1, height: 36, background: '#1a1c22', color: '#e8e8ea', border: '1px solid #2a2d36', borderRadius: 7 }}>Stop</button>
|
||
</div>
|
||
</div>
|
||
</Win>
|
||
);
|
||
};
|
||
const StField = ({ label, value, flex, width }) => (
|
||
<label style={{ flex, width }}>
|
||
<div style={{ fontSize: 11, color: '#777a86', marginBottom: 4 }}>{label}</div>
|
||
<div style={{ height: 32, background: '#15171c', border: '1px solid #1f2228', borderRadius: 7, padding: '0 10px', display: 'flex', alignItems: 'center', fontSize: 13 }}>{value}</div>
|
||
</label>
|
||
);
|
||
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
// 10 · Pipeline / DAG — visualize the test chain as a flow
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
const SketchPipeline = () => {
|
||
const f = "'Inter', system-ui, sans-serif";
|
||
const m = "'JetBrains Mono', monospace";
|
||
const Node = ({ x, y, label, status, sub }) => {
|
||
const c = status === 'pass' ? '#21a655' : status === 'fail' ? '#e85a4f' : status === 'run' ? '#1e6fd9' : '#aaa';
|
||
const bg = status === 'pass' ? '#e8f7ee' : status === 'fail' ? '#fdebe9' : status === 'run' ? '#eaf1fb' : '#f4f4f4';
|
||
return (
|
||
<g transform={`translate(${x},${y})`}>
|
||
<rect width="120" height="40" rx="6" fill={bg} stroke={c} strokeWidth="1.5" />
|
||
<circle cx="14" cy="20" r="5" fill={c} />
|
||
<text x="26" y="17" fontSize="11" fontWeight="600" fill="#1a1a1a" fontFamily={f}>{label}</text>
|
||
<text x="26" y="30" fontSize="9.5" fill="#666" fontFamily={m}>{sub}</text>
|
||
</g>
|
||
);
|
||
};
|
||
const Arrow = ({ from, to, dashed }) => (
|
||
<line x1={from[0]} y1={from[1]} x2={to[0]} y2={to[1]} stroke="#bbb" strokeWidth="1.5" strokeDasharray={dashed ? '3 3' : ''} markerEnd="url(#ar)" />
|
||
);
|
||
return (
|
||
<Win bg="#fafafa" radius={6}>
|
||
<div style={{ height: 30, display: 'flex', alignItems: 'center', padding: '0 12px', borderBottom: '1px solid #ececec', fontFamily: f, fontSize: 12 }}>
|
||
<Mark color="#1a1a1a" size={12} />
|
||
<span style={{ marginLeft: 8, fontWeight: 600 }}>Drover-Go</span>
|
||
<span style={{ marginLeft: 8, color: '#888', fontSize: 11 }}>· pipeline view</span>
|
||
<TitleButtons />
|
||
</div>
|
||
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
|
||
{/* compact endpoint */}
|
||
<div style={{ padding: 10, display: 'flex', gap: 6, alignItems: 'center', borderBottom: '1px solid #ececec', background: '#fff' }}>
|
||
<span style={{ fontSize: 11, color: '#888', fontFamily: m }}>SOCKS5</span>
|
||
<input style={{ flex: 1, height: 26, padding: '0 8px', border: '1px solid #d0d0d0', borderRadius: 4, fontFamily: m, fontSize: 11.5 }} defaultValue="95.165.72.59" />
|
||
<input style={{ width: 70, height: 26, padding: '0 8px', border: '1px solid #d0d0d0', borderRadius: 4, fontFamily: m, fontSize: 11.5 }} defaultValue="12334" />
|
||
<button style={{ height: 26, padding: '0 12px', background: '#1e6fd9', color: '#fff', border: 'none', borderRadius: 4, fontWeight: 600, fontSize: 11.5 }}>Run ▸</button>
|
||
</div>
|
||
{/* DAG */}
|
||
<div style={{ flex: 1, padding: 12, background: 'radial-gradient(circle at 1px 1px, #ddd 1px, transparent 0) 0 0/16px 16px', overflow: 'auto' }}>
|
||
<svg width="456" height="380" viewBox="0 0 456 380">
|
||
<defs>
|
||
<marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto"><path d="M0 0 L10 5 L0 10 z" fill="#bbb"/></marker>
|
||
</defs>
|
||
<Node x={20} y={20} label="TCP reach" sub="12 ms" status="pass" />
|
||
<Arrow from={[140, 40]} to={[160, 40]} />
|
||
<Node x={160} y={20} label="SOCKS5 greet" sub="ok" status="pass" />
|
||
<Arrow from={[280, 40]} to={[300, 40]} />
|
||
<Node x={300} y={20} label="SOCKS5 auth" sub="ok" status="pass" />
|
||
<Arrow from={[360, 60]} to={[360, 90]} />
|
||
<Node x={300} y={90} label="CONNECT discord" sub="38 ms" status="pass" />
|
||
<Arrow from={[300, 110]} to={[180, 160]} />
|
||
<Arrow from={[300, 110]} to={[180, 220]} />
|
||
<Node x={60} y={140} label="UDP ASSOCIATE" sub="running…" status="run" />
|
||
<Arrow from={[180, 160]} to={[60, 240]} dashed />
|
||
<Node x={60} y={220} label="UDP STUN RTT" sub="pending" status="pending" />
|
||
<Node x={60} y={300} label="Discord API" sub="pending" status="pending" />
|
||
<Arrow from={[180, 240]} to={[60, 320]} dashed />
|
||
</svg>
|
||
</div>
|
||
<div style={{ padding: 10, borderTop: '1px solid #ececec', background: '#fff', display: 'flex', gap: 6, alignItems: 'center' }}>
|
||
<span style={{ fontSize: 11, color: '#1e6fd9', fontFamily: m }}>● 4/7 running</span>
|
||
<span style={{ marginLeft: 'auto', display: 'flex', gap: 6 }}>
|
||
<button style={{ height: 28, padding: '0 14px', background: '#fff', border: '1px solid #ddd', borderRadius: 4, fontSize: 12 }}>Cancel</button>
|
||
<button style={{ height: 28, padding: '0 14px', background: '#1e6fd9', color: '#fff', border: 'none', borderRadius: 4, fontSize: 12, fontWeight: 600, opacity: 0.5 }}>Start proxying</button>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</Win>
|
||
);
|
||
};
|
||
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
// 11 · Bauhaus — geometric blocks, primary colors, asymmetric grid
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
const SketchBauhaus = () => {
|
||
const f = "'Space Grotesk', sans-serif";
|
||
return (
|
||
<Win bg="#f4ede0" radius={0} ring="#000">
|
||
<div style={{ height: 32, display: 'flex', alignItems: 'center', padding: '0 12px', borderBottom: '3px solid #000', fontFamily: f, fontSize: 13, fontWeight: 700 }}>
|
||
<span style={{ width: 14, height: 14, borderRadius: '50%', background: '#e63946' }} />
|
||
<span style={{ width: 14, height: 14, background: '#1d3557', marginLeft: 6 }} />
|
||
<span style={{ width: 0, height: 0, borderLeft: '7px solid transparent', borderRight: '7px solid transparent', borderBottom: '14px solid #f1c40f', marginLeft: 6 }} />
|
||
<span style={{ marginLeft: 12 }}>DROVER-GO</span>
|
||
<span style={{ marginLeft: 'auto', fontWeight: 400, fontSize: 11 }}>v0.4.2 · ⚙ · — · ×</span>
|
||
</div>
|
||
<div style={{ flex: 1, padding: 12, fontFamily: f, color: '#000', display: 'grid', gridTemplateColumns: '1fr 1fr', gridTemplateRows: 'auto 1fr auto', gap: 8 }}>
|
||
<div style={{ gridColumn: '1 / -1', background: '#1d3557', color: '#fff', padding: 14, border: '3px solid #000' }}>
|
||
<div style={{ fontSize: 10, letterSpacing: '.15em', opacity: .7 }}>FORM 01</div>
|
||
<div style={{ fontSize: 18, fontWeight: 700, marginTop: 2 }}>SOCKS5 PROXY</div>
|
||
<div style={{ display: 'flex', gap: 8, marginTop: 12 }}>
|
||
<BaField flex={1} label="HOST" value="95.165.72.59" dark />
|
||
<BaField width={84} label="PORT" value="12334" dark />
|
||
</div>
|
||
</div>
|
||
<button style={{ background: '#e63946', color: '#fff', border: '3px solid #000', padding: '10px', fontFamily: f, fontSize: 13, fontWeight: 700, letterSpacing: '.06em' }}>CHECK →</button>
|
||
<button style={{ background: '#f1c40f', color: '#000', border: '3px solid #000', padding: '10px', fontFamily: f, fontSize: 13, fontWeight: 700, letterSpacing: '.06em' }}>● START</button>
|
||
<div style={{ gridColumn: '1 / -1', border: '3px solid #000', background: '#fff', padding: 14, minHeight: 0, overflow: 'hidden' }}>
|
||
<div style={{ fontSize: 10, letterSpacing: '.15em', opacity: .6 }}>STATUS · 7/7</div>
|
||
<div style={{ fontSize: 16, fontWeight: 700, marginBottom: 10 }}>ALL CHECKS PASSED</div>
|
||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '4px 14px' }}>
|
||
{[
|
||
['TCP reach', '12ms'],
|
||
['SOCKS5 greet', 'ok'],
|
||
['SOCKS5 auth', 'ok'],
|
||
['CONNECT', '38ms'],
|
||
['UDP assoc', 'ok'],
|
||
['UDP STUN', '24ms'],
|
||
['API', '200'],
|
||
].map(([n, v], i) => (
|
||
<div key={i} style={{ display: 'flex', alignItems: 'center', fontSize: 12, borderBottom: '1px solid #000' }}>
|
||
<span style={{ width: 8, height: 8, background: '#e63946', marginRight: 8 }} />
|
||
<span style={{ flex: 1 }}>{n}</span>
|
||
<span style={{ fontWeight: 700 }}>{v}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</Win>
|
||
);
|
||
};
|
||
const BaField = ({ label, value, flex, width, dark }) => (
|
||
<label style={{ flex, width }}>
|
||
<div style={{ fontSize: 9.5, marginBottom: 3, opacity: .7, letterSpacing: '.1em' }}>{label}</div>
|
||
<div style={{ height: 30, background: dark ? '#fff' : '#000', color: dark ? '#000' : '#fff', border: '2px solid #000', padding: '0 8px', display: 'flex', alignItems: 'center', fontSize: 13, fontWeight: 600 }}>{value}</div>
|
||
</label>
|
||
);
|
||
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
// 12 · Brutalist — hard borders, mono, acid lime
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
const SketchBrutalist = () => {
|
||
const m = "'Geist Mono', 'JetBrains Mono', monospace";
|
||
return (
|
||
<Win bg="#f3f1e8" radius={0} ring="#000">
|
||
<div style={{ height: 38, display: 'flex', alignItems: 'center', padding: '0 12px', borderBottom: '2px solid #000', fontFamily: m, fontSize: 12, color: '#000', background: '#e8e4d2' }}>
|
||
<span style={{ fontWeight: 700 }}>▶ DROVER-GO</span>
|
||
<span style={{ marginLeft: 8, fontSize: 10 }}>v0.4.2</span>
|
||
<div style={{ marginLeft: 'auto', display: 'flex', gap: 6, fontSize: 14, fontWeight: 700 }}>
|
||
<span>[⚙]</span><span>[—]</span><span>[X]</span>
|
||
</div>
|
||
</div>
|
||
<div style={{ padding: 14, fontFamily: m, fontSize: 12, color: '#000', flex: 1, display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||
<div style={{ border: '2px solid #000', background: '#fff', padding: 10 }}>
|
||
<div style={{ fontSize: 11, fontWeight: 700, marginBottom: 8 }}>// SOCKS5 PROXY</div>
|
||
<div style={{ display: 'flex', gap: 6 }}>
|
||
<BrField flex={1} label="HOST" value="95.165.72.59" />
|
||
<BrField width={92} label="PORT" value="12334" />
|
||
</div>
|
||
<div style={{ marginTop: 10, fontSize: 12 }}>[X] AUTHENTICATION</div>
|
||
<div style={{ display: 'flex', gap: 6, marginTop: 8 }}>
|
||
<BrField flex={1} label="LOGIN" value="alice" />
|
||
<BrField flex={1} label="PASSWORD" value="********" />
|
||
</div>
|
||
<button style={{ marginTop: 12, width: '100%', height: 36, background: '#c5f74d', border: '2px solid #000', boxShadow: '4px 4px 0 #000', fontFamily: m, fontWeight: 700, fontSize: 13, cursor: 'pointer' }}>► CHECK CONNECTION</button>
|
||
</div>
|
||
<div style={{ border: '2px solid #000', background: '#fff', padding: 10, flex: 1 }}>
|
||
<div style={{ fontSize: 11, fontWeight: 700, marginBottom: 8 }}>// STATUS · 7/7 OK</div>
|
||
{[
|
||
['[OK]', 'TCP_REACHABILITY', '12ms'],
|
||
['[OK]', 'SOCKS5_GREETING', 'ok'],
|
||
['[OK]', 'SOCKS5_AUTH', 'ok'],
|
||
['[OK]', 'CONNECT_DISCORD', '38ms'],
|
||
['[OK]', 'UDP_ASSOCIATE', 'relay 1.2.3.4'],
|
||
['[OK]', 'UDP_STUN_RTT', '24ms'],
|
||
['[OK]', 'DISCORD_API', '200'],
|
||
].map(([s, n, v], i) => (
|
||
<div key={i} style={{ display: 'flex', padding: '3px 0', borderBottom: i < 6 ? '1px dashed #999' : 'none' }}>
|
||
<span style={{ width: 38, fontWeight: 700 }}>{s}</span>
|
||
<span style={{ flex: 1 }}>{n}</span>
|
||
<span>{v}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<div style={{ display: 'flex', gap: 6 }}>
|
||
<button style={{ flex: 1, height: 38, background: '#c5f74d', border: '2px solid #000', boxShadow: '4px 4px 0 #000', fontFamily: m, fontWeight: 700 }}>► START</button>
|
||
<button style={{ flex: 1, height: 38, background: '#fff', border: '2px solid #000', boxShadow: '4px 4px 0 #000', fontFamily: m, fontWeight: 700 }}>■ STOP</button>
|
||
</div>
|
||
</div>
|
||
</Win>
|
||
);
|
||
};
|
||
const BrField = ({ label, value, flex, width }) => (
|
||
<label style={{ flex, width }}>
|
||
<div style={{ fontSize: 9.5, marginBottom: 3 }}>{label}</div>
|
||
<div style={{ height: 30, border: '2px solid #000', padding: '0 8px', display: 'flex', alignItems: 'center', fontSize: 12.5, background: '#fff' }}>{value}</div>
|
||
</label>
|
||
);
|
||
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
// SketchesApp
|
||
// ═════════════════════════════════════════════════════════════════════════
|
||
|
||
const SKETCHES = [
|
||
{ id: 'fluent', label: '01 · Fluent / Win11 native', el: <SketchFluent /> },
|
||
{ id: 'sidebar', label: '02 · IDE sidebar (VS Code-ish)', el: <SketchSidebar /> },
|
||
{ id: 'wizard', label: '03 · Wizard / stepper', el: <SketchWizard /> },
|
||
{ id: 'monitor', label: '04 · Network monitor (gauges)', el: <SketchMonitor /> },
|
||
{ id: 'inspector', label: '05 · Inspector / properties', el: <SketchInspector /> },
|
||
{ id: 'palette', label: '06 · Command palette (cmd+k)', el: <SketchPalette /> },
|
||
{ id: 'hero', label: '07 · Big toggle (VPN-style)', el: <SketchHero /> },
|
||
{ id: 'toolbar', label: '08 · Toolbar / ribbon (classic)', el: <SketchToolbar /> },
|
||
{ id: 'studio', label: '09 · Studio (Linear-ish)', el: <SketchStudio /> },
|
||
{ id: 'pipeline', label: '10 · Pipeline / DAG view', el: <SketchPipeline /> },
|
||
{ id: 'bauhaus', label: '11 · Bauhaus blocks', el: <SketchBauhaus /> },
|
||
{ id: 'brutalist', label: '12 · Brutalist (mono+lime)', el: <SketchBrutalist /> },
|
||
];
|
||
|
||
const SketchesApp = () => (
|
||
<DesignCanvas>
|
||
<DCSection id="about" title="Drover-Go" subtitle="12 directions — every one looks/works like real software, not a marketing site. Different UI paradigms (sidebar, wizard, palette, ribbon, DAG, gauges…), not just different palettes.">
|
||
<div data-dc-static style={{ width: 720, padding: '20px 24px', background: '#fff', borderRadius: 10, boxShadow: '0 0 0 1px rgba(0,0,0,.06)', fontSize: 13, lineHeight: 1.55, color: '#3a3530' }}>
|
||
<div style={{ fontWeight: 600, marginBottom: 8, fontSize: 14 }}>What changed</div>
|
||
<p style={{ margin: '0 0 8px' }}>Removed the "marketing" stylings (ticket, synthwave, soft pastel, editorial serif). Replaced with software-native paradigms — each card explores a <em>different way to organize the same UI</em>, not just a different paint job:</p>
|
||
<ul style={{ margin: '0 0 8px', paddingLeft: 18 }}>
|
||
<li><b>Layout patterns:</b> sidebar IDE · wizard/stepper · pipeline/DAG · ribbon · palette+form · big-toggle hero</li>
|
||
<li><b>Element vocab:</b> gauges + sparklines · key/value inspector · fieldsets · tabs+breadcrumbs · status bar · toolbar buttons</li>
|
||
<li><b>Aesthetic:</b> Fluent (Win11) · classic Office · Linear/Studio · brutalist · Bauhaus blocks</li>
|
||
</ul>
|
||
<p style={{ margin: 0, color: '#7a7065' }}>Tell me which paradigm/aesthetic combo to take forward.</p>
|
||
</div>
|
||
</DCSection>
|
||
<DCSection id="sketches" title="12 directions" subtitle="Drag to reorder. Click any artboard's expand icon to focus.">
|
||
{SKETCHES.map(s => (
|
||
<DCArtboard key={s.id} id={s.id} label={s.label} width={W} height={H}>
|
||
{s.el}
|
||
</DCArtboard>
|
||
))}
|
||
</DCSection>
|
||
</DesignCanvas>
|
||
);
|
||
|
||
// Expose individual sketches so other files (e.g. shortlist) can render them.
|
||
Object.assign(window, {
|
||
SketchFluent, SketchSidebar, SketchWizard, SketchMonitor,
|
||
SketchInspector, SketchPalette, SketchHero, SketchToolbar,
|
||
SketchStudio, SketchPipeline, SketchBauhaus, SketchBrutalist,
|
||
});
|