ckonteos80 commited on
Commit
fe6fbbd
·
1 Parent(s): 230c39c

Add status panel and HuggingFace polling

Browse files

Introduce a status panel to index.html that displays initialization state for backend Spaces (Chat Engine and Memory Extractor). Adds CSS for the panel, status rows/dots and disabled play-button styling. Replaces the direct Enter link with a disabled Enter button that is enabled once both Spaces report RUNNING. Implements JS polling: queries the Hugging Face Spaces API for runtime.stage, triggers no-cors wake requests, falls back to direct pings after repeated Hub API failures, and updates visual states (waking, ready, error) accordingly.

Files changed (1) hide show
  1. index.html +188 -1
index.html CHANGED
@@ -163,6 +163,84 @@
163
  font-size: 1.2em;
164
  }
165
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  </style>
167
  </head>
168
  <body>
@@ -187,10 +265,119 @@
187
  </ul>
188
  </div>
189
 
190
- <a href="./game/index.html" class="play-button">Enter</a>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
 
192
  <div class="quote">"L'enfer, c'est les autres" — Jean-Paul Sartre</div>
193
  </div>
194
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  </body>
196
  </html>
 
163
  font-size: 1.2em;
164
  }
165
  }
166
+
167
+ .status-panel {
168
+ margin: 35px auto 0;
169
+ max-width: 480px;
170
+ border: 1px solid rgba(212, 175, 55, 0.2);
171
+ border-radius: 6px;
172
+ padding: 18px 24px;
173
+ background: rgba(0, 0, 0, 0.4);
174
+ font-family: 'Helvetica Neue', Arial, sans-serif;
175
+ }
176
+
177
+ .status-panel-title {
178
+ font-size: 0.8em;
179
+ color: #777;
180
+ text-transform: uppercase;
181
+ letter-spacing: 2px;
182
+ margin-bottom: 14px;
183
+ }
184
+
185
+ .status-row {
186
+ display: flex;
187
+ align-items: center;
188
+ gap: 12px;
189
+ padding: 9px 0;
190
+ }
191
+
192
+ .status-row + .status-row {
193
+ border-top: 1px solid rgba(255, 255, 255, 0.05);
194
+ }
195
+
196
+ .status-dot {
197
+ width: 10px;
198
+ height: 10px;
199
+ border-radius: 50%;
200
+ flex-shrink: 0;
201
+ background: #444;
202
+ transition: background 0.4s ease, box-shadow 0.4s ease;
203
+ }
204
+
205
+ .status-dot.waking {
206
+ background: #D4AF37;
207
+ box-shadow: 0 0 7px rgba(212, 175, 55, 0.7);
208
+ animation: hf-pulse 1.3s ease-in-out infinite;
209
+ }
210
+
211
+ .status-dot.ready {
212
+ background: #4caf50;
213
+ box-shadow: 0 0 7px rgba(76, 175, 80, 0.7);
214
+ }
215
+
216
+ .status-dot.error { background: #c0392b; }
217
+
218
+ @keyframes hf-pulse {
219
+ 0%, 100% { opacity: 1; }
220
+ 50% { opacity: 0.35; }
221
+ }
222
+
223
+ .status-label {
224
+ flex: 1;
225
+ font-size: 1.05em;
226
+ color: #c8c8c8;
227
+ }
228
+
229
+ .status-text {
230
+ font-size: 0.85em;
231
+ color: #666;
232
+ transition: color 0.4s ease;
233
+ }
234
+
235
+ .status-text.ready { color: #4caf50; }
236
+ .status-text.error { color: #c0392b; }
237
+
238
+ .play-button.disabled {
239
+ opacity: 0.35;
240
+ cursor: not-allowed;
241
+ pointer-events: none;
242
+ box-shadow: none;
243
+ }
244
  </style>
245
  </head>
246
  <body>
 
265
  </ul>
266
  </div>
267
 
268
+ <div class="status-panel">
269
+ <div class="status-panel-title">Initializing Systems</div>
270
+ <div class="status-row">
271
+ <div class="status-dot" id="dot-chat"></div>
272
+ <span class="status-label">Chat Engine</span>
273
+ <span class="status-text" id="status-chat">Checking…</span>
274
+ </div>
275
+ <div class="status-row">
276
+ <div class="status-dot" id="dot-memory"></div>
277
+ <span class="status-label">Memory Extractor</span>
278
+ <span class="status-text" id="status-memory">Checking…</span>
279
+ </div>
280
+ </div>
281
+
282
+ <a href="./game/index.html" class="play-button disabled" id="enter-btn">Enter</a>
283
 
284
  <div class="quote">"L'enfer, c'est les autres" — Jean-Paul Sartre</div>
285
  </div>
286
  </div>
287
+ <script>
288
+ const SPACES = [
289
+ {
290
+ id: 'chat',
291
+ owner: 'jejunepixels',
292
+ repo: 'noexit-proxy',
293
+ wakeUrl: 'https://jejunepixels-noexit-proxy.hf.space',
294
+ ready: false
295
+ },
296
+ {
297
+ id: 'memory',
298
+ owner: 'jejunepixels',
299
+ repo: 'qwen3-0-6b-info-extractor-api',
300
+ wakeUrl: 'https://jejunepixels-qwen3-0-6b-info-extractor-api.hf.space/extract',
301
+ ready: false
302
+ }
303
+ ];
304
+
305
+ function setStatus(space, state, text) {
306
+ space.dotEl.className = 'status-dot ' + state;
307
+ space.statusEl.textContent = text;
308
+ space.statusEl.className = 'status-text' +
309
+ (state === 'ready' ? ' ready' : state === 'error' ? ' error' : '');
310
+ }
311
+
312
+ function checkAllReady(enterBtn, titleEl) {
313
+ if (SPACES.every(s => s.ready)) {
314
+ enterBtn.classList.remove('disabled');
315
+ titleEl.textContent = 'Systems Ready';
316
+ }
317
+ }
318
+
319
+ async function pollSpace(space, enterBtn, titleEl) {
320
+ const apiUrl = 'https://huggingface.co/api/spaces/' + space.owner + '/' + space.repo;
321
+ let hubFailCount = 0;
322
+
323
+ while (true) {
324
+ // After 2 Hub API failures, fall back to direct ping
325
+ if (hubFailCount < 2) {
326
+ try {
327
+ const res = await fetch(apiUrl);
328
+ if (!res.ok) throw new Error('HTTP ' + res.status);
329
+ const data = await res.json();
330
+ const stage = data && data.runtime && data.runtime.stage;
331
+ hubFailCount = 0;
332
+
333
+ if (stage === 'RUNNING') {
334
+ setStatus(space, 'ready', 'Ready');
335
+ space.ready = true;
336
+ checkAllReady(enterBtn, titleEl);
337
+ return;
338
+ } else if (stage === 'SLEEPING' || stage === 'PAUSED') {
339
+ setStatus(space, 'waking', 'Waking up…');
340
+ fetch(space.wakeUrl, { mode: 'no-cors' }).catch(function() {});
341
+ } else if (stage === 'BUILDING' || stage === 'APP_STARTING') {
342
+ setStatus(space, 'waking', 'Starting up…');
343
+ } else if (stage === 'STOPPED') {
344
+ setStatus(space, 'error', 'Unavailable');
345
+ return;
346
+ } else {
347
+ setStatus(space, 'waking', 'Initializing…');
348
+ }
349
+ } catch (e) {
350
+ hubFailCount++;
351
+ setStatus(space, 'waking', 'Waking up…');
352
+ }
353
+ } else {
354
+ // Direct no-cors ping — resolves if server is reachable, throws if truly down
355
+ try {
356
+ setStatus(space, 'waking', 'Waking up…');
357
+ fetch(space.wakeUrl, { mode: 'no-cors' }).catch(function() {});
358
+ await fetch(space.wakeUrl, { mode: 'no-cors' });
359
+ setStatus(space, 'ready', 'Ready');
360
+ space.ready = true;
361
+ checkAllReady(enterBtn, titleEl);
362
+ return;
363
+ } catch (e) {
364
+ setStatus(space, 'waking', 'Waking up…');
365
+ }
366
+ }
367
+ await new Promise(function(r) { setTimeout(r, 5000); });
368
+ }
369
+ }
370
+
371
+ document.addEventListener('DOMContentLoaded', function() {
372
+ var enterBtn = document.getElementById('enter-btn');
373
+ var titleEl = document.querySelector('.status-panel-title');
374
+
375
+ SPACES.forEach(function(space) {
376
+ space.dotEl = document.getElementById('dot-' + space.id);
377
+ space.statusEl = document.getElementById('status-' + space.id);
378
+ pollSpace(space, enterBtn, titleEl);
379
+ });
380
+ });
381
+ </script>
382
  </body>
383
  </html>