File size: 8,653 Bytes
b64654b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Shellular</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<!-- ββ Login page βββββββββββββββββββββββββββββββββββββββββββ -->
<div id="login-page" class="page">
<div class="login-card">
<div class="brand">
<svg class="brand-icon" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="10" fill="#00c4cc"/>
<path d="M8 14l8 6-8 6" stroke="#fff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<line x1="20" y1="26" x2="32" y2="26" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/>
</svg>
<h1>Shellular</h1>
</div>
<p class="tagline">Enter your access key to get the QR code</p>
<form id="login-form" autocomplete="off">
<div class="field">
<label for="key-input">Access Key</label>
<div class="input-wrap">
<input
id="key-input"
type="password"
placeholder="β’β’β’β’β’β’β’β’β’β’β’β’"
autocomplete="current-password"
spellcheck="false"
/>
<button type="button" id="toggle-vis" class="eye-btn" aria-label="Toggle visibility">
<svg id="eye-open" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
<circle cx="12" cy="12" r="3"/>
</svg>
<svg id="eye-closed" class="hidden" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94"/>
<path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19"/>
<line x1="1" y1="1" x2="23" y2="23"/>
</svg>
</button>
</div>
</div>
<button type="submit" id="login-btn">
<span id="login-label">Login</span>
<span id="login-spinner" class="spinner hidden"></span>
</button>
<p id="login-error" class="error-msg hidden"></p>
</form>
</div>
</div>
<!-- ββ Dashboard page βββββββββββββββββββββββββββββββββββββββ -->
<div id="dashboard-page" class="page hidden">
<header class="topbar">
<div class="topbar-brand">
<svg class="brand-icon small" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="10" fill="#00c4cc"/>
<path d="M8 14l8 6-8 6" stroke="#fff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<line x1="20" y1="26" x2="32" y2="26" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/>
</svg>
<span>Shellular</span>
</div>
<div class="topbar-actions">
<span id="status-badge" class="badge badge-stopped">Stopped</span>
<button id="restart-btn" class="btn btn-secondary" title="Restart shellular">↻ Restart</button>
<button id="logout-btn" class="btn btn-ghost">Logout</button>
</div>
</header>
<main class="dashboard">
<!-- QR card -->
<section class="card qr-card">
<div class="card-header">
<h2>QR Code</h2>
<p>Scan with the <strong>Shellular app</strong> to connect your device</p>
</div>
<div id="qr-area" class="qr-area">
<!-- Loading state -->
<div id="qr-loading" class="qr-state">
<div class="loader"></div>
<p>Starting Shellular…</p>
</div>
<!-- QR code rendered on canvas via qrcode.js -->
<div id="qr-ready" class="qr-state hidden">
<div class="qr-frame">
<div id="qr-canvas"></div>
</div>
<p class="qr-hint">Point your Shellular app camera at the code above</p>
</div>
<!-- Error / stopped state -->
<div id="qr-error" class="qr-state hidden">
<p class="error-icon">⚠</p>
<p id="qr-error-msg">Shellular stopped unexpectedly.</p>
<button class="btn btn-primary" onclick="restartShellular()">Try again</button>
</div>
</div>
</section>
<!-- First-time setup card (hidden once secrets are saved) -->
<section id="setup-card" class="card setup-card hidden">
<div class="card-header">
<h2>⚡ One-time Setup</h2>
<p>Save these as HF Secrets so restarts never need to re-register</p>
</div>
<div class="setup-body">
<p class="setup-intro">
Shellular just registered successfully. To make this permanent
(and avoid rate-limit errors after container restarts), add the
three secrets below to your Space:
<a href="https://huggingface.co/spaces/settings" target="_blank" class="setup-link">
Space Settings β Variables and secrets
</a>
</p>
<div class="secret-row">
<span class="secret-name">SHELLULAR_HOST_ID</span>
<code id="val-host-id" class="secret-val"></code>
<button class="btn-copy" data-target="val-host-id">Copy</button>
</div>
<div class="secret-row">
<span class="secret-name">SHELLULAR_MACHINE_ID</span>
<code id="val-machine-id" class="secret-val"></code>
<button class="btn-copy" data-target="val-machine-id">Copy</button>
</div>
<div class="secret-row">
<span class="secret-name">SHELLULAR_KEY</span>
<code id="val-key" class="secret-val"></code>
<button class="btn-copy" data-target="val-key">Copy</button>
</div>
<p class="setup-note">
After adding all three, restart this Space. This panel will disappear once the secrets are detected.
</p>
</div>
</section>
<!-- Manual registration fallback (shown when rate-limited) -->
<section id="manual-reg-card" class="card manual-card hidden">
<div class="card-header">
<h2>⚠ Rate Limited β Manual Registration</h2>
<p>The shellular relay rejected automatic registration. Run one command from your terminal.</p>
</div>
<div class="manual-body">
<p class="manual-intro">
The Shellular registration API is temporarily rate-limiting this server's IP.
You can bypass it by registering from <strong>your own machine</strong> β it only takes 10 seconds.
</p>
<div class="manual-step">
<span class="step-num">1</span>
<div>
<p>Run this in your terminal (Mac / Linux / Windows WSL):</p>
<div class="code-block">
<code id="manual-curl-cmd">Loadingβ¦</code>
<button class="btn-copy" data-target="manual-curl-cmd">Copy</button>
</div>
</div>
</div>
<div class="manual-step">
<span class="step-num">2</span>
<div>
<p>You'll get back something like <code class="inline-code">{"success":true,"data":{"hostId":"<strong>XXXX</strong>"}}</code></p>
<p>Paste the <strong>hostId</strong> value below and click <strong>Connect</strong>:</p>
<div class="manual-input-row">
<input id="manual-host-id" type="text" placeholder='e.g. M58FBHn3YzbN' spellcheck="false" />
<button id="manual-submit-btn" class="btn btn-primary manual-btn">Connect</button>
</div>
<p id="manual-error" class="error-msg hidden"></p>
</div>
</div>
</div>
</section>
<!-- Log card -->
<section class="card log-card">
<div class="card-header">
<h2>Output</h2>
<button id="clear-log-btn" class="btn btn-ghost small">Clear</button>
</div>
<pre id="log-pre" class="log-pre"></pre>
</section>
</main>
</div>
<script src="qrcode.min.js"></script>
<script src="app.js"></script>
</body>
</html>
|