Files

245 lines
18 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SciMesh Worker · Setup</title>
<style>
:root{
--bg:#0b0e13;--panel:#11151d;--panel-2:#161c27;--border:#1f2634;--border-soft:#181f2b;
--text:#e9ecf3;--text-2:#98a2b5;--text-3:#5d6879;
--accent:#5b8cff;--accent-soft:#5b8cff1f;--accent-strong:#3f6fe0;
--green:#3fce8a;--green-soft:#3fce8a1a;--amber:#e5b64f;--amber-soft:#e5b64f1a;--red:#f2647c;--red-soft:#f2647c1a;
--mono:ui-monospace,"SF Mono",SFMono-Regular,Menlo,monospace;
color-scheme:dark;
}
*{box-sizing:border-box;margin:0;padding:0}
body{background:radial-gradient(900px 500px at 50% -180px,#16233d66,transparent),var(--bg);color:var(--text);font:14px/1.55 Inter,-apple-system,"Segoe UI",Roboto,sans-serif;-webkit-font-smoothing:antialiased;min-height:100vh}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
input{font:inherit;color:var(--text);background:var(--panel-2);border:1px solid var(--border);border-radius:9px;padding:10px 13px;width:100%;outline:none;transition:border-color .12s,box-shadow .12s}
input:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
input::placeholder{color:var(--text-3)}
code{font-family:var(--mono);font-size:.86em}
.shell{max-width:660px;margin:0 auto;padding:44px 22px 70px}
.brand{display:flex;align-items:center;justify-content:center;gap:11px;margin-bottom:8px}
.brand-mark{display:grid;place-items:center;width:34px;height:34px;border-radius:10px;background:linear-gradient(135deg,#5b8cff,#7c5cff);box-shadow:0 4px 16px #5b8cff40}
.brand-mark svg{width:18px;height:18px;stroke:#fff}
.brand-name{font-weight:700;font-size:16px;letter-spacing:-.01em}
.brand-name span{color:var(--text-3);font-weight:500}
.tagline{text-align:center;color:var(--text-3);font-size:12.5px;margin-bottom:34px}
.tagline code{color:var(--text-2)}
/* step indicator */
.steps{display:flex;align-items:center;justify-content:center;gap:0;margin-bottom:30px}
.step{display:flex;flex-direction:column;align-items:center;gap:7px;width:96px}
.step-dot{display:grid;place-items:center;width:30px;height:30px;border-radius:50%;border:1.5px solid var(--border);background:var(--panel);color:var(--text-3);font-size:12.5px;font-weight:700;transition:all .2s}
.step-label{font-size:11px;font-weight:600;color:var(--text-3);letter-spacing:.02em}
.step.active .step-dot{border-color:var(--accent);background:var(--accent-soft);color:var(--accent);box-shadow:0 0 0 4px #5b8cff14}
.step.active .step-label{color:var(--text)}
.step.done .step-dot{border-color:var(--green);background:var(--green-soft);color:var(--green)}
.step.done .step-label{color:var(--text-2)}
.step-line{flex:1;max-width:44px;height:1.5px;background:var(--border);margin:0 6px 22px;position:relative;overflow:hidden}
.step-line.done:after{content:"";position:absolute;inset:0;background:var(--green)}
.card{background:var(--panel);border:1px solid var(--border-soft);border-radius:15px;padding:26px 28px;box-shadow:0 24px 60px #0000004d}
.card h1{font-size:18px;font-weight:700;letter-spacing:-.02em;margin-bottom:4px}
.card .sub{color:var(--text-2);font-size:13px;margin-bottom:22px}
.field{margin-bottom:16px}
.field label{display:block;font-size:12px;font-weight:650;letter-spacing:.04em;text-transform:uppercase;color:var(--text-3);margin-bottom:7px}
.field .hint{margin-top:6px;font-size:12px;color:var(--text-3)}
.field .hint code{color:var(--text-2)}
.radio-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.radio-card{border:1px solid var(--border);border-radius:11px;padding:13px 14px;cursor:pointer;transition:all .13s;background:var(--panel-2)}
.radio-card:hover{border-color:#2a3446}
.radio-card.sel{border-color:var(--accent);background:var(--accent-soft);box-shadow:0 0 0 3px #5b8cff14}
.radio-card b{display:flex;align-items:center;gap:8px;font-size:13.5px}
.radio-card b svg{width:15px;height:15px;stroke:var(--accent)}
.radio-card p{margin-top:4px;font-size:12px;color:var(--text-2)}
.check-row{display:flex;align-items:center;gap:12px;padding:11px 14px;border:1px solid var(--border-soft);border-radius:10px;margin-bottom:9px;background:var(--panel-2)}
.check-ic{display:grid;place-items:center;width:24px;height:24px;border-radius:50%;flex:none}
.check-ic svg{width:13px;height:13px;stroke-width:2.6}
.check-ok{background:var(--green-soft)}.check-ok svg{stroke:var(--green)}
.check-bad{background:var(--red-soft)}.check-bad svg{stroke:var(--red)}
.check-wait{background:#ffffff10}.check-wait svg{stroke:var(--text-3)}
.check-row b{font-size:13.5px;font-weight:600}
.check-row span{display:block;font-size:12px;color:var(--text-3)}
.check-row .ms{margin-left:auto;font:11.5px var(--mono);color:var(--text-3)}
.actions{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:24px}
.btn{display:inline-flex;align-items:center;gap:8px;border-radius:9px;padding:10px 18px;font-weight:650;font-size:13.5px;border:1px solid transparent;transition:all .13s}
.btn svg{width:15px;height:15px;stroke:currentColor}
.btn-primary{background:var(--accent);color:#0a1222}
.btn-primary:hover{background:var(--accent-strong);color:#fff}
.btn-ghost{border-color:var(--border);color:var(--text-2);background:var(--panel-2)}
.btn-ghost:hover{color:var(--text);border-color:#2a3446}
.btn-danger{background:var(--red-soft);color:var(--red)}
.btn-lg{padding:12px 24px;font-size:14.5px;border-radius:10px}
.link{color:var(--text-3);font-size:13px}
.link:hover{color:var(--text)}
/* status view */
.status-head{display:flex;align-items:center;gap:14px;margin-bottom:22px}
.pulse{position:relative;width:12px;height:12px;border-radius:50%;background:var(--green);flex:none}
.pulse:after{content:"";position:absolute;inset:-5px;border-radius:50%;border:2px solid var(--green);opacity:.5;animation:ping 1.6s ease-out infinite}
@keyframes ping{from{transform:scale(.6);opacity:.7}to{transform:scale(1.4);opacity:0}}
.status-head h1{font-size:19px}
.status-head .sub{font-size:12.5px;color:var(--text-3)}
.stat-row{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:18px}
.stat{background:var(--panel-2);border:1px solid var(--border-soft);border-radius:11px;padding:12px 14px}
.stat b{display:block;font-size:20px;font-weight:700;letter-spacing:-.02em}
.stat span{font-size:11px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--text-3)}
.stat.bad b{color:var(--red)}
.logbox{background:#0a0d12;border:1px solid var(--border-soft);border-radius:11px;padding:14px 16px;font:12px/1.7 var(--mono);color:#8fa3bf;max-height:190px;overflow-y:auto}
.logbox .t{color:var(--text-3)}
.logbox .ok{color:var(--green)}
.logbox .hl{color:#c9d6ea}
.meta-line{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px}
.chip{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--border);border-radius:99px;padding:4px 11px;font-size:12px;color:var(--text-2);background:var(--panel-2)}
.chip svg{width:12px;height:12px;stroke:var(--text-3)}
.wizard-page{display:none}.wizard-page.active{display:block;animation:fade .18s ease}
@keyframes fade{from{opacity:0;transform:translateY(5px)}to{opacity:1}}
.demo-toggle{position:fixed;bottom:14px;right:16px;font-size:11.5px;color:var(--text-3);background:var(--panel);border:1px solid var(--border);border-radius:99px;padding:6px 13px;opacity:.8}
.demo-toggle:hover{color:var(--text)}
</style>
</head>
<body>
<div class="shell">
<div class="brand">
<div class="brand-mark"><svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round"><circle cx="6" cy="6" r="2.4"/><circle cx="18" cy="7" r="2.4"/><circle cx="12" cy="17" r="2.4"/><path d="M8 7.5l7.6-1M7 8.3l3.7 6.5M16.6 9.2l-3.2 5.6"/></svg></div>
<div class="brand-name">SciMesh <span>· Worker setup</span></div>
</div>
<p class="tagline">Local wizard served by <code>worker-agent setup</code> · <code>127.0.0.1:12700</code></p>
<!-- ═══ WIZARD VIEW ═══ -->
<div id="view-wizard">
<div class="steps">
<div class="step done" id="st1"><div class="step-dot"></div><div class="step-label">Connect</div></div>
<div class="step-line done" id="sl1"></div>
<div class="step active" id="st2"><div class="step-dot">2</div><div class="step-label">Machine</div></div>
<div class="step-line" id="sl2"></div>
<div class="step" id="st3"><div class="step-dot">3</div><div class="step-label">Check</div></div>
<div class="step-line" id="sl3"></div>
<div class="step" id="st4"><div class="step-dot">4</div><div class="step-label">Run</div></div>
</div>
<!-- step 1 (kept in DOM for the mockup; step 2 shown) -->
<div class="wizard-page" id="wp1">
<div class="card">
<h1>Connect to a coordinator</h1>
<p class="sub">The coordinator hands out work and collects results. Ask your cluster admin for its address.</p>
<div class="field"><label>Coordinator URL</label><input value="http://192.168.1.10:8080" placeholder="http://192.168.1.10:8080"><p class="hint">For a served instance this is the address printed by <code>coordinator serve</code>.</p></div>
<div class="field"><label>Authentication</label>
<div class="radio-grid">
<div class="radio-card sel"><b><svg viewBox="0 0 24 24" fill="none" stroke-width="2"><rect x="4" y="10" width="16" height="10" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg>Cluster token</b><p>Serve instances: one token for every worker.</p></div>
<div class="radio-card"><b><svg viewBox="0 0 24 24" fill="none" stroke-width="2"><circle cx="8" cy="14" r="4"/><path d="M10.8 11.2L20 2M15 4l3 3"/></svg>Worker key</b><p>Shared clusters: a key tied to your account.</p></div>
</div>
</div>
<div class="field"><label>Token</label><input type="password" value="sm_live_9f2c7ab41d06e8f3c5a7"><p class="hint">The admin can copy it from <code>coordinator token</code> on the server.</p></div>
<div class="actions"><span class="link">Step 1 of 4</span><button class="btn btn-primary" onclick="goto(2)">Continue →</button></div>
</div>
</div>
<!-- step 2 -->
<div class="wizard-page active" id="wp2">
<div class="card">
<h1>This machine</h1>
<p class="sub">Where tasks run and how the machine appears in the cluster.</p>
<div class="field"><label>Worker name</label><input value="emil-laptop"><p class="hint">Shown in the coordinators worker list.</p></div>
<div class="field"><label>Work directory</label><input value="/home/emil/scimesh-worker"><p class="hint">Datasets and shard results live here. ~1 GB free space recommended.</p></div>
<div class="field"><label>Compute resources advertised</label>
<div class="radio-grid">
<div class="radio-card sel"><b>8 CPUs · 16 GB</b><p>Detected automatically.</p></div>
<div class="radio-card"><b>Custom…</b><p>Limit what this machine advertises.</p></div>
</div>
</div>
<div class="actions"><button class="btn btn-ghost" onclick="goto(1)">← Back</button><button class="btn btn-primary" onclick="goto(3)">Continue →</button></div>
</div>
</div>
<!-- step 3 -->
<div class="wizard-page" id="wp3">
<div class="card">
<h1>Preflight check</h1>
<p class="sub">Making sure this machine can reach the coordinator and run SciMesh workloads.</p>
<div class="check-row"><div class="check-ic check-ok"><svg viewBox="0 0 24 24" fill="none" stroke-linecap="round"><path d="M4 12l5 5L20 6"/></svg></div><div><b>Coordinator reachable</b><span>http://192.168.1.10:8080 · coordinator 1.1.0-alpha.1</span></div><span class="ms">34 ms</span></div>
<div class="check-row"><div class="check-ic check-ok"><svg viewBox="0 0 24 24" fill="none" stroke-linecap="round"><path d="M4 12l5 5L20 6"/></svg></div><div><b>Token accepted</b><span>authenticated as cluster worker</span></div><span class="ms">51 ms</span></div>
<div class="check-row"><div class="check-ic check-ok"><svg viewBox="0 0 24 24" fill="none" stroke-linecap="round"><path d="M4 12l5 5L20 6"/></svg></div><div><b>Python 3.12 found</b><span>/usr/bin/python3</span></div><span class="ms">8 ms</span></div>
<div class="check-row"><div class="check-ic check-bad"><svg viewBox="0 0 24 24" fill="none" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg></div><div><b>scimesh package missing</b><span>run: pip install scimesh — or let the wizard install it</span></div><button class="btn btn-ghost" style="margin-left:auto;padding:6px 12px;font-size:12px">Install</button></div>
<div class="actions"><button class="btn btn-ghost" onclick="goto(2)">← Back</button><button class="btn btn-primary" onclick="goto(4)">Continue anyway →</button></div>
</div>
</div>
<!-- step 4 -->
<div class="wizard-page" id="wp4">
<div class="card" style="text-align:center;padding:40px 28px">
<div class="brand-mark" style="margin:0 auto 18px;width:46px;height:46px;border-radius:13px"><svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" style="width:22px;height:22px"><path d="M6 4l14 8-14 8V4z"/></svg></div>
<h1 style="font-size:20px">Ready to join the cluster</h1>
<p class="sub" style="max-width:380px;margin:8px auto 26px">The wizard will save your configuration to <code>~/.scimesh-worker/config.json</code> and start the worker as a background process.</p>
<div class="meta-line" style="justify-content:center">
<span class="chip"><svg viewBox="0 0 24 24" fill="none" stroke-width="2"><circle cx="12" cy="12" r="9"/></svg>emil-laptop</span>
<span class="chip"><svg viewBox="0 0 24 24" fill="none" stroke-width="2"><path d="M4 6h16M4 12h16M4 18h16"/></svg>8 CPUs · 16 GB</span>
<span class="chip"><svg viewBox="0 0 24 24" fill="none" stroke-width="2"><path d="M12 3l8 4.5v9L12 21l-8-4.5v-9L12 3z"/></svg>4 workloads</span>
</div>
<div class="actions" style="justify-content:space-between;margin-top:30px"><button class="btn btn-ghost" onclick="goto(3)">← Back</button><button class="btn btn-primary btn-lg" onclick="showStatus()">Start worker</button></div>
</div>
</div>
</div>
<!-- ═══ STATUS VIEW ═══ -->
<div id="view-status" style="display:none">
<div class="card">
<div class="status-head">
<div class="pulse"></div>
<div><h1>emil-laptop is working</h1><div class="sub">worker id <code>w-01HZZ4T9C6</code> · registered with 192.168.1.10:8080 · up 42 min</div></div>
<button class="btn btn-danger" style="margin-left:auto"><svg viewBox="0 0 24 24" fill="none" stroke-width="2"><rect x="6" y="6" width="12" height="12" rx="2"/></svg>Stop</button>
</div>
<div class="stat-row">
<div class="stat"><b>14</b><span>claimed</span></div>
<div class="stat"><b>12</b><span>completed</span></div>
<div class="stat bad"><b>0</b><span>failed</span></div>
<div class="stat"><b>2.1s</b><span>avg shard</span></div>
</div>
<div class="meta-line">
<span class="chip">similarity-search</span><span class="chip">similarity-graph</span><span class="chip">descriptors</span><span class="chip">molwt-filter</span>
<span class="chip" style="margin-left:auto"><svg viewBox="0 0 24 24" fill="none" stroke-width="2"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 3"/></svg>heartbeat 2 s ago</span>
</div>
<div class="logbox">
<div><span class="t">14:02:11</span> agent 1.1.0-alpha.1 · config ~/.scimesh-worker/config.json</div>
<div><span class="t">14:02:11</span> coordinator http://192.168.1.10:8080 · token auth</div>
<div><span class="t">14:02:12</span> <span class="ok">registered</span> as <span class="hl">emil-laptop</span> (w-01HZZ4T9C6)</div>
<div><span class="t">14:02:12</span> capabilities: similarity-search, similarity-graph, descriptors, molwt-filter</div>
<div><span class="t">14:03:40</span> claimed task <span class="hl">7f3a…c91e/007</span> · similarity-search</div>
<div><span class="t">14:03:42</span> <span class="ok">completed</span> 7f3a…c91e/007 · uploaded shard (2.1s)</div>
<div><span class="t">14:05:03</span> claimed task <span class="hl">7f3a…c91e/031</span> · similarity-search</div>
<div><span class="t">14:05:05</span> <span class="ok">completed</span> 7f3a…c91e/031 · uploaded shard (1.9s)</div>
</div>
<div class="actions"><span class="link">Configuration: <code>~/.scimesh-worker/config.json</code></span><button class="btn btn-ghost" onclick="showWizard()">Reconfigure…</button></div>
</div>
</div>
</div>
<button class="demo-toggle" onclick="toggleView()">mockup · toggle wizard / status</button>
<script>
const pages=['wp1','wp2','wp3','wp4'];
function goto(n){
pages.forEach((id,i)=>document.getElementById(id).classList.toggle('active',i===n-1));
for(let i=1;i<=4;i++){
const st=document.getElementById('st'+i);
st.classList.toggle('done',i<n);st.classList.toggle('active',i===n);
st.querySelector('.step-dot').textContent=i<n?'✓':i;
if(i<4)document.getElementById('sl'+i).classList.toggle('done',i<n);
}
}
function showStatus(){document.getElementById('view-wizard').style.display='none';document.getElementById('view-status').style.display='block'}
function showWizard(){document.getElementById('view-wizard').style.display='block';document.getElementById('view-status').style.display='none'}
function toggleView(){document.getElementById('view-status').style.display==='none'?showStatus():showWizard()}
document.querySelectorAll('.radio-grid').forEach(g=>g.querySelectorAll('.radio-card').forEach(c=>c.addEventListener('click',()=>{g.querySelectorAll('.radio-card').forEach(x=>x.classList.remove('sel'));c.classList.add('sel')})));
</script>
</body>
</html>