docs/design/v2: add 12-variant React design archive
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>
This commit is contained in:
2026-05-01 03:12:02 +03:00
parent 5da30ad058
commit 113616b039
20 changed files with 6566 additions and 0 deletions
+249
View File
@@ -0,0 +1,249 @@
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8"/>
<title>Drover-Go — Desktop GUI explorations</title>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<style>
html, body { margin: 0; padding: 0; background: #f0eee9; font-family: -apple-system, "Segoe UI", system-ui, sans-serif; }
</style>
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
</head>
<body>
<template id="__bundler_thumbnail" data-bg-color="#f0eee9">
<svg viewBox="0 0 1200 800" xmlns="http://www.w3.org/2000/svg">
<rect width="1200" height="800" fill="#f0eee9"/>
<g transform="translate(600 400)">
<circle r="160" fill="none" stroke="#2a7d76" stroke-width="22"/>
<path d="M -90 -50 L 0 40 L 90 -50" fill="none" stroke="#2a7d76" stroke-width="22" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M 0 40 L 0 130" fill="none" stroke="#2a7d76" stroke-width="22" stroke-linecap="round"/>
</g>
<text x="600" y="660" text-anchor="middle" font-family="ui-monospace, monospace" font-size="36" font-weight="700" fill="#2a251f" letter-spacing="6">DROVER-GO</text>
</svg>
</template>
<div id="root"></div>
<script type="text/babel" src="design-canvas.jsx"></script>
<script type="text/babel" src="drover-shared.jsx"></script>
<script type="text/babel" src="drover-classic.jsx"></script>
<script type="text/babel" src="drover-minimal.jsx"></script>
<script type="text/babel" src="drover-glass.jsx"></script>
<script type="text/babel" src="drover-brutalist.jsx"></script>
<script type="text/babel">
const { useState, useEffect } = React;
// For each variant we want to show several states side-by-side. To get
// distinct states without manual interaction in the artboards, we use a
// small "preset" wrapper that performs scripted setup against the variant's
// useDrover hook by lifting it up: we pass an `initial` form and an
// imperative control prop that the variant doesn't need — instead we just
// re-mount with different presets to land at idle / checking / passed /
// active / failed / failed+active states.
//
// We achieve this with a small Driver component that wraps the variant and
// drives it through actions on mount.
function Driver({ Component, mode, preset }) {
// preset: 'idle' | 'checking' | 'passed' | 'active' | 'failed' | 'active-warn' | 'auth-passed'
const ref = React.useRef(null);
const [boot, setBoot] = React.useState(false);
React.useEffect(() => { setBoot(true); }, []);
return (
<PresetHost Component={Component} mode={mode} preset={preset}/>
);
}
// Re-implements the variant render but injects a controller that drives
// the inner useDrover state via portal. Simpler approach: forward an
// `onReady` ref to the inner D state — but the variants don't expose D.
// Easiest working approach: wrap each variant and intercept by re-mounting
// through React + run actions via a *parallel* useDrover instance is not
// possible. So: we expose a tiny "preset" API directly inside each variant
// via the `initial` argument that mutates on first paint.
// The variants all accept an `initial` form. We extend the contract: the
// variants now also accept `__preset` and `__scenario` to seed the state
// machine on mount. To do that without rewriting all four files, we use a
// shim: PresetVariant wraps a variant and uses an effect on the first
// render of the inner DOM to dispatch synthetic clicks/programmatic state
// via a "Programmatic" pattern: each variant exposes its useDrover-result
// through a global hook map. Cleaner: just patch useDrover to broadcast.
// ── implementation: monkey-patch useDrover to record latest D per slot ──
if (!window.__drvSlots) {
const orig = window.useDrover;
window.__drvSlots = new Map();
window.useDrover = function(initial) {
const D = orig(initial);
const slot = (initial && initial.__slot) || null;
React.useEffect(() => {
if (slot) window.__drvSlots.set(slot, D);
});
return D;
};
}
function PresetHost({ Component, mode, preset }) {
const slot = React.useId();
React.useEffect(() => {
let cancelled = false;
const wait = () => new Promise(r => requestAnimationFrame(r));
(async () => {
await wait(); await wait();
const D = window.__drvSlots.get(slot);
if (!D || cancelled) return;
if (preset === 'idle') return;
if (preset === 'checking') {
// Set scenario, kick check, but don't await — we want it mid-flight
D.runCheck();
return;
}
if (preset === 'passed') {
D.setLogsOpen?.(false);
D.runCheck();
// wait for completion
for (let i = 0; i < 60 && !cancelled; i++) {
await new Promise(r => setTimeout(r, 200));
const cur = window.__drvSlots.get(slot);
if (cur?.phase === 'checked') break;
}
return;
}
if (preset === 'auth-passed') {
D.update({ auth: true, login: 'drover', password: 'secret123' });
await wait();
const cur1 = window.__drvSlots.get(slot);
cur1.runCheck();
for (let i = 0; i < 60 && !cancelled; i++) {
await new Promise(r => setTimeout(r, 200));
const cur = window.__drvSlots.get(slot);
if (cur?.phase === 'checked') break;
}
return;
}
if (preset === 'failed') {
// We need to switch scenario before runCheck.
D.setScenario?.('udpFail');
await wait();
const cur1 = window.__drvSlots.get(slot);
cur1.runCheck();
for (let i = 0; i < 60 && !cancelled; i++) {
await new Promise(r => setTimeout(r, 200));
const cur = window.__drvSlots.get(slot);
if (cur?.phase === 'checked') break;
}
return;
}
if (preset === 'active') {
D.runCheck();
for (let i = 0; i < 60 && !cancelled; i++) {
await new Promise(r => setTimeout(r, 200));
const cur = window.__drvSlots.get(slot);
if (cur?.phase === 'checked') break;
}
await wait();
window.__drvSlots.get(slot)?.startProxy?.();
// let stats accumulate for a bit
await new Promise(r => setTimeout(r, 1500));
return;
}
if (preset === 'active-warn') {
D.setScenario?.('udpFail');
await wait();
window.__drvSlots.get(slot)?.runCheck?.();
for (let i = 0; i < 60 && !cancelled; i++) {
await new Promise(r => setTimeout(r, 200));
const cur = window.__drvSlots.get(slot);
if (cur?.phase === 'checked') break;
}
await wait();
window.__drvSlots.get(slot)?.startProxy?.();
await new Promise(r => setTimeout(r, 1500));
return;
}
})();
return () => { cancelled = true; window.__drvSlots.delete(slot); };
}, [preset, slot]);
return <Component mode={mode} initial={{ __slot: slot }} />;
}
// ─── Section header card with reasoning ────────────────────────────────
function ReasoningCard() {
return (
<div style={{
width: 520, padding: '16px 18px',
background: '#ffffff', border: '1px solid rgba(0,0,0,0.08)', borderRadius: 10,
fontSize: 13, lineHeight: 1.55, color: '#2a251f',
boxShadow: '0 1px 3px rgba(0,0,0,0.04)',
}}>
<div style={{ fontWeight: 700, marginBottom: 4, letterSpacing: 0.2 }}>Drover-Go · GUI explorations</div>
<div style={{ color: '#5c5650' }}>
Один экран · 480×640 · фикс. размер для Win11. Четыре стилистики, dark + light, по нескольку состояний:
idle checking all-passed active. Отдельные борды показывают сценарий с провалом UDP
и активный режим с предупреждением. Все артборды живые: кликабельны, имеют focus / hover / disabled.
</div>
</div>
);
}
function App() {
const { DesignCanvas, DCSection, DCArtboard } = window;
const W = 480, H = 640;
const variants = [
{ id: 'classic', Component: window.ClassicWindow, title: 'Classic devtool',
subtitle: 'Hairlines, dense mono metrics. Сдержанный teal-акцент. Под аудиторию sysadmin/devs.' },
{ id: 'minimal', Component: window.MinimalWindow, title: 'Minimal · Fluent',
subtitle: 'Под Windows 11. Карточки с микро-границей, мягкий slate-blue, system feel.' },
{ id: 'glass', Component: window.GlassWindow, title: 'Glassmorphism',
subtitle: 'Frosted blur поверх градиента, glow на active. Для тех, кому хочется vibe.' },
{ id: 'brutal', Component: window.BrutWindow, title: 'Brutalist',
subtitle: 'Жёсткие границы, без скруглений, моно. Acid-lime акцент, hard-shadow на кнопках.' },
];
const presets = [
{ id: 'idle', label: 'idle (dark)', mode: 'dark', preset: 'idle' },
{ id: 'idle-light', label: 'idle (light)', mode: 'light', preset: 'idle' },
{ id: 'checking', label: 'checking… (dark)', mode: 'dark', preset: 'checking' },
{ id: 'passed', label: 'all passed (dark)', mode: 'dark', preset: 'passed' },
{ id: 'auth-passed', label: 'with auth (light)', mode: 'light', preset: 'auth-passed' },
{ id: 'failed', label: 'UDP failed (light)', mode: 'light', preset: 'failed' },
{ id: 'active', label: 'active proxy (dark)', mode: 'dark', preset: 'active' },
{ id: 'active-warn', label: 'active · UDP warn (light)', mode: 'light', preset: 'active-warn' },
];
return (
<>
<DesignCanvas>
<DCSection id="intro" title="About" subtitle="Brief & approach">
<DCArtboard id="intro-card" label="reasoning" width={520} height={140}>
<ReasoningCard/>
</DCArtboard>
</DCSection>
{variants.map(v => (
<DCSection key={v.id} id={v.id} title={v.title} subtitle={v.subtitle}>
{presets.map(p => (
<DCArtboard key={p.id} id={`${v.id}-${p.id}`} label={p.label} width={W} height={H}>
<PresetHost Component={v.Component} mode={p.mode} preset={p.preset}/>
</DCArtboard>
))}
</DCSection>
))}
</DesignCanvas>
</>
);
}
// Wait until all script files loaded
function tryMount() {
if (window.ClassicWindow && window.MinimalWindow && window.GlassWindow && window.BrutWindow && window.DesignCanvas) {
ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
} else {
setTimeout(tryMount, 50);
}
}
tryMount();
</script>
</body>
</html>