Spaces:
Running
Running
Upload 2 files
Browse files- README.md +1 -1
- index.html +68 -5
README.md
CHANGED
|
@@ -43,7 +43,7 @@ Type freely to converse with two characters whose names, backstories, and person
|
|
| 43 |
|
| 44 |
### AI Pipeline
|
| 45 |
|
| 46 |
-
All AI calls route through Hugging Face Spaces.
|
| 47 |
|
| 48 |
#### Dialogue Model
|
| 49 |
**Endpoint:** `jejunepixels-noexit-proxy.hf.space/chat`
|
|
|
|
| 43 |
|
| 44 |
### AI Pipeline
|
| 45 |
|
| 46 |
+
All AI calls route through Hugging Face Spaces. Visitors can play free up to a per-visitor token allowance; after that, or from the start if they prefer, they sign in with their Hugging Face account and dialogue and character generation run on their own Inference Providers credits. No API keys are exposed to the client.
|
| 47 |
|
| 48 |
#### Dialogue Model
|
| 49 |
**Endpoint:** `jejunepixels-noexit-proxy.hf.space/chat`
|
index.html
CHANGED
|
@@ -360,12 +360,13 @@
|
|
| 360 |
<button type="button" class="signin-btn" id="signin-btn">
|
| 361 |
<img src="https://huggingface.co/datasets/huggingface/badges/resolve/main/sign-in-with-huggingface-xl-dark.svg" alt="Sign in with Hugging Face">
|
| 362 |
</button>
|
| 363 |
-
<p class="auth-note">
|
| 364 |
-
<p class="auth-note" id="auth-newtab-note" hidden>
|
| 365 |
</div>
|
| 366 |
<div id="auth-signed-in" hidden>
|
| 367 |
<p class="auth-user">Signed in as <strong id="auth-username"></strong> · <a href="#" id="signout-link">Sign out</a></p>
|
| 368 |
<p class="auth-note">Dialogue is billed to your Hugging Face inference credits.</p>
|
|
|
|
| 369 |
</div>
|
| 370 |
<p class="auth-error" id="auth-error" hidden></p>
|
| 371 |
</div>
|
|
@@ -400,18 +401,29 @@
|
|
| 400 |
(state === 'ready' ? ' ready' : state === 'error' ? ' error' : '');
|
| 401 |
}
|
| 402 |
|
| 403 |
-
// Set by the sign-in script below
|
|
|
|
| 404 |
var signedIn = false;
|
|
|
|
| 405 |
|
| 406 |
function checkAllReady(enterBtn, titleEl) {
|
| 407 |
var systemsReady = SPACES.every(s => s.ready);
|
| 408 |
if (systemsReady) titleEl.textContent = 'Systems Ready';
|
| 409 |
-
enterBtn.classList.toggle('disabled', !(systemsReady && signedIn));
|
|
|
|
|
|
|
|
|
|
|
|
|
| 410 |
}
|
| 411 |
|
| 412 |
window.setSignedIn = function(value) {
|
| 413 |
signedIn = value;
|
| 414 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 415 |
};
|
| 416 |
|
| 417 |
async function pollSpace(space, enterBtn, titleEl) {
|
|
@@ -483,9 +495,16 @@
|
|
| 483 |
// inference is billed to the signed-in player.
|
| 484 |
const STORAGE_KEY = 'noexit_hf_oauth';
|
| 485 |
const HUB_URL = 'https://cdn.jsdelivr.net/npm/@huggingface/hub@2.17.5/+esm';
|
|
|
|
|
|
|
|
|
|
|
|
|
| 486 |
|
| 487 |
// The Hugging Face login page cannot load inside the Space's frame on
|
| 488 |
// huggingface.co, so when framed the flow moves to a new tab on this URL.
|
|
|
|
|
|
|
|
|
|
| 489 |
const framed = window.self !== window.top;
|
| 490 |
const pageUrl = window.location.origin + window.location.pathname;
|
| 491 |
const params = new URLSearchParams(window.location.search);
|
|
@@ -518,6 +537,31 @@
|
|
| 518 |
window.setSignedIn(!!auth);
|
| 519 |
}
|
| 520 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 521 |
let hub = null;
|
| 522 |
async function loadHub() {
|
| 523 |
if (!hub) hub = await import(HUB_URL);
|
|
@@ -530,6 +574,9 @@
|
|
| 530 |
window.open(pageUrl + '?signin=1', '_blank', 'noopener');
|
| 531 |
return;
|
| 532 |
}
|
|
|
|
|
|
|
|
|
|
| 533 |
const vars = window.huggingface && window.huggingface.variables;
|
| 534 |
if (!vars || !vars.OAUTH_CLIENT_ID) {
|
| 535 |
showError('Sign-in is not set up for this Space yet.');
|
|
@@ -569,6 +616,22 @@
|
|
| 569 |
}
|
| 570 |
|
| 571 |
document.getElementById('auth-newtab-note').hidden = !framed;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 572 |
document.getElementById('signin-btn').addEventListener('click', startSignIn);
|
| 573 |
document.getElementById('signout-link').addEventListener('click', function(event) {
|
| 574 |
event.preventDefault();
|
|
|
|
| 360 |
<button type="button" class="signin-btn" id="signin-btn">
|
| 361 |
<img src="https://huggingface.co/datasets/huggingface/badges/resolve/main/sign-in-with-huggingface-xl-dark.svg" alt="Sign in with Hugging Face">
|
| 362 |
</button>
|
| 363 |
+
<p class="auth-note" id="auth-free-note">Play free for a while, or sign in with Hugging Face to use your own inference credits — no API key needed.</p>
|
| 364 |
+
<p class="auth-note" id="auth-newtab-note" hidden>The game and sign-in open in a new tab.</p>
|
| 365 |
</div>
|
| 366 |
<div id="auth-signed-in" hidden>
|
| 367 |
<p class="auth-user">Signed in as <strong id="auth-username"></strong> · <a href="#" id="signout-link">Sign out</a></p>
|
| 368 |
<p class="auth-note">Dialogue is billed to your Hugging Face inference credits.</p>
|
| 369 |
+
<p class="auth-note" id="auth-return-note" hidden>You're signed in — go back to the game tab to continue.</p>
|
| 370 |
</div>
|
| 371 |
<p class="auth-error" id="auth-error" hidden></p>
|
| 372 |
</div>
|
|
|
|
| 401 |
(state === 'ready' ? ' ready' : state === 'error' ? ' error' : '');
|
| 402 |
}
|
| 403 |
|
| 404 |
+
// Set by the sign-in script below: a visitor can play when they are signed in
|
| 405 |
+
// with Hugging Face, or while they still have free play left on the proxy.
|
| 406 |
var signedIn = false;
|
| 407 |
+
var freePlay = false;
|
| 408 |
|
| 409 |
function checkAllReady(enterBtn, titleEl) {
|
| 410 |
var systemsReady = SPACES.every(s => s.ready);
|
| 411 |
if (systemsReady) titleEl.textContent = 'Systems Ready';
|
| 412 |
+
enterBtn.classList.toggle('disabled', !(systemsReady && (signedIn || freePlay)));
|
| 413 |
+
}
|
| 414 |
+
|
| 415 |
+
function refreshEnter() {
|
| 416 |
+
checkAllReady(document.getElementById('enter-btn'), document.querySelector('.status-panel-title'));
|
| 417 |
}
|
| 418 |
|
| 419 |
window.setSignedIn = function(value) {
|
| 420 |
signedIn = value;
|
| 421 |
+
refreshEnter();
|
| 422 |
+
};
|
| 423 |
+
|
| 424 |
+
window.setFreePlay = function(value) {
|
| 425 |
+
freePlay = value;
|
| 426 |
+
refreshEnter();
|
| 427 |
};
|
| 428 |
|
| 429 |
async function pollSpace(space, enterBtn, titleEl) {
|
|
|
|
| 495 |
// inference is billed to the signed-in player.
|
| 496 |
const STORAGE_KEY = 'noexit_hf_oauth';
|
| 497 |
const HUB_URL = 'https://cdn.jsdelivr.net/npm/@huggingface/hub@2.17.5/+esm';
|
| 498 |
+
const FREE_STATUS_URL = 'https://jejunepixels-noexit-proxy.hf.space/free/status';
|
| 499 |
+
// Set when this page was opened by the game's own sign-in prompt, and kept
|
| 500 |
+
// across the trip to the Hugging Face login page.
|
| 501 |
+
const FROM_GAME_KEY = 'noexit_signin_from_game';
|
| 502 |
|
| 503 |
// The Hugging Face login page cannot load inside the Space's frame on
|
| 504 |
// huggingface.co, so when framed the flow moves to a new tab on this URL.
|
| 505 |
+
// The game opens in a new tab too: a framed page gets separate browser
|
| 506 |
+
// storage, and the game must share storage with the sign-in page to pick
|
| 507 |
+
// up a sign-in made while playing.
|
| 508 |
const framed = window.self !== window.top;
|
| 509 |
const pageUrl = window.location.origin + window.location.pathname;
|
| 510 |
const params = new URLSearchParams(window.location.search);
|
|
|
|
| 537 |
window.setSignedIn(!!auth);
|
| 538 |
}
|
| 539 |
|
| 540 |
+
// Ask the proxy whether this visitor still has free play. The proxy may be
|
| 541 |
+
// waking up, so keep trying for a while; until it answers, Enter needs sign-in.
|
| 542 |
+
async function checkFreePlay() {
|
| 543 |
+
const noteEl = document.getElementById('auth-free-note');
|
| 544 |
+
for (let attempt = 0; attempt < 24; attempt++) {
|
| 545 |
+
try {
|
| 546 |
+
const res = await fetch(FREE_STATUS_URL);
|
| 547 |
+
if (res.ok) {
|
| 548 |
+
const status = await res.json();
|
| 549 |
+
const available = !!status.enabled && status.remaining > 0;
|
| 550 |
+
window.setFreePlay(available);
|
| 551 |
+
if (!available) {
|
| 552 |
+
noteEl.textContent = status.enabled
|
| 553 |
+
? 'Your free play is used up — sign in with Hugging Face to continue on your own inference credits.'
|
| 554 |
+
: 'Sign in with Hugging Face to play on your own inference credits — no API key needed.';
|
| 555 |
+
}
|
| 556 |
+
return;
|
| 557 |
+
}
|
| 558 |
+
if (res.status === 404) break; // proxy without free play
|
| 559 |
+
} catch (e) {}
|
| 560 |
+
await new Promise(function(r) { setTimeout(r, 5000); });
|
| 561 |
+
}
|
| 562 |
+
noteEl.textContent = 'Sign in with Hugging Face to play on your own inference credits — no API key needed.';
|
| 563 |
+
}
|
| 564 |
+
|
| 565 |
let hub = null;
|
| 566 |
async function loadHub() {
|
| 567 |
if (!hub) hub = await import(HUB_URL);
|
|
|
|
| 574 |
window.open(pageUrl + '?signin=1', '_blank', 'noopener');
|
| 575 |
return;
|
| 576 |
}
|
| 577 |
+
if (params.get('from') === 'game') {
|
| 578 |
+
try { sessionStorage.setItem(FROM_GAME_KEY, '1'); } catch (e) {}
|
| 579 |
+
}
|
| 580 |
const vars = window.huggingface && window.huggingface.variables;
|
| 581 |
if (!vars || !vars.OAUTH_CLIENT_ID) {
|
| 582 |
showError('Sign-in is not set up for this Space yet.');
|
|
|
|
| 616 |
}
|
| 617 |
|
| 618 |
document.getElementById('auth-newtab-note').hidden = !framed;
|
| 619 |
+
|
| 620 |
+
if (framed) {
|
| 621 |
+
// Whether the visitor may play is settled in the game's own tab.
|
| 622 |
+
document.getElementById('enter-btn').target = '_blank';
|
| 623 |
+
window.setFreePlay(true);
|
| 624 |
+
} else {
|
| 625 |
+
checkFreePlay();
|
| 626 |
+
}
|
| 627 |
+
|
| 628 |
+
// Sign-in started from the game's prompt: point the player back to that tab.
|
| 629 |
+
try {
|
| 630 |
+
if (auth && (params.get('from') === 'game' || sessionStorage.getItem(FROM_GAME_KEY))) {
|
| 631 |
+
document.getElementById('auth-return-note').hidden = false;
|
| 632 |
+
sessionStorage.removeItem(FROM_GAME_KEY);
|
| 633 |
+
}
|
| 634 |
+
} catch (e) {}
|
| 635 |
document.getElementById('signin-btn').addEventListener('click', startSignIn);
|
| 636 |
document.getElementById('signout-link').addEventListener('click', function(event) {
|
| 637 |
event.preventDefault();
|