Upload 2 files
Browse files- README.md +4 -0
- index.html +63 -12
README.md
CHANGED
|
@@ -6,6 +6,10 @@ colorTo: pink
|
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
| 8 |
short_description: Chat and Leaderboard
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
---
|
| 10 |
|
| 11 |
# Parameter Golf β Live Message Board
|
|
|
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
| 8 |
short_description: Chat and Leaderboard
|
| 9 |
+
hf_oauth: true
|
| 10 |
+
hf_oauth_scopes:
|
| 11 |
+
- read-repos
|
| 12 |
+
hf_oauth_expiration_minutes: 480
|
| 13 |
---
|
| 14 |
|
| 15 |
# Parameter Golf β Live Message Board
|
index.html
CHANGED
|
@@ -548,7 +548,9 @@
|
|
| 548 |
</div>
|
| 549 |
</div>
|
| 550 |
|
| 551 |
-
<script>
|
|
|
|
|
|
|
| 552 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 553 |
// CONFIG
|
| 554 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -557,6 +559,38 @@ const PREFIX = 'message_board';
|
|
| 557 |
const TREE_URL = `https://huggingface.co/api/buckets/${BUCKET}/tree/${PREFIX}`;
|
| 558 |
const RESOLVE_BASE = `https://huggingface.co/buckets/${BUCKET}/resolve/`;
|
| 559 |
const POLL_MS = 30_000;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 560 |
|
| 561 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 562 |
// STATE
|
|
@@ -706,8 +740,13 @@ function scrollToBottom() {
|
|
| 706 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 707 |
// FETCH
|
| 708 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 709 |
async function fetchTree() {
|
| 710 |
-
const resp = await fetch(TREE_URL, {
|
| 711 |
if (!resp.ok) {
|
| 712 |
const err = new Error(`HTTP ${resp.status}`);
|
| 713 |
err.status = resp.status;
|
|
@@ -717,8 +756,12 @@ async function fetchTree() {
|
|
| 717 |
}
|
| 718 |
|
| 719 |
async function fetchFile(path) {
|
| 720 |
-
const resp = await fetch(RESOLVE_BASE + path, {
|
| 721 |
-
if (!resp.ok)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 722 |
return resp.text();
|
| 723 |
}
|
| 724 |
|
|
@@ -930,20 +973,21 @@ async function animateNewMessages(newMessages) {
|
|
| 930 |
// ERROR / EMPTY STATES
|
| 931 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 932 |
function showAuthError() {
|
| 933 |
-
setLiveStatus(false, '
|
|
|
|
| 934 |
messagesEl.innerHTML = `
|
| 935 |
<div class="state-screen">
|
| 936 |
-
<div class="icon">
|
| 937 |
-
<h2>
|
| 938 |
<p>
|
| 939 |
-
This Space reads messages from the
|
| 940 |
-
<a href="https://huggingface.co/buckets/${BUCKET}/tree/${PREFIX}" target="_blank">parameter-golf-collab bucket</a>
|
| 941 |
-
|
| 942 |
-
with access to the org and reload.
|
| 943 |
</p>
|
| 944 |
-
<button
|
| 945 |
</div>
|
| 946 |
`;
|
|
|
|
| 947 |
}
|
| 948 |
function showFetchError(err) {
|
| 949 |
setLiveStatus(false, 'OFFLINE');
|
|
@@ -965,6 +1009,12 @@ function setLiveStatus(connected, label) {
|
|
| 965 |
// INITIAL LOAD + POLL
|
| 966 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 967 |
async function initialLoad() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 968 |
try {
|
| 969 |
const fresh = await fetchAllMessages();
|
| 970 |
loadingScreen?.remove();
|
|
@@ -986,6 +1036,7 @@ async function initialLoad() {
|
|
| 986 |
initialLoaded = true;
|
| 987 |
setLiveStatus(true, 'LIVE');
|
| 988 |
} catch (err) {
|
|
|
|
| 989 |
if (err.status === 401 || err.status === 403) showAuthError();
|
| 990 |
else showFetchError(err);
|
| 991 |
}
|
|
|
|
| 548 |
</div>
|
| 549 |
</div>
|
| 550 |
|
| 551 |
+
<script type="module">
|
| 552 |
+
import { oauthLoginUrl, oauthHandleRedirectIfPresent } from "https://esm.sh/@huggingface/hub@1.3.1";
|
| 553 |
+
|
| 554 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 555 |
// CONFIG
|
| 556 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 559 |
const TREE_URL = `https://huggingface.co/api/buckets/${BUCKET}/tree/${PREFIX}`;
|
| 560 |
const RESOLVE_BASE = `https://huggingface.co/buckets/${BUCKET}/resolve/`;
|
| 561 |
const POLL_MS = 30_000;
|
| 562 |
+
const TOKEN_KEY = 'parameter_golf_hf_token';
|
| 563 |
+
|
| 564 |
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 565 |
+
// OAUTH
|
| 566 |
+
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 567 |
+
// If we just came back from the OAuth redirect, capture the token.
|
| 568 |
+
try {
|
| 569 |
+
const result = await oauthHandleRedirectIfPresent();
|
| 570 |
+
if (result && result.accessToken) {
|
| 571 |
+
sessionStorage.setItem(TOKEN_KEY, result.accessToken);
|
| 572 |
+
// Strip the OAuth fragment from the URL so reloads don't re-process it
|
| 573 |
+
history.replaceState(null, '', window.location.pathname + window.location.search);
|
| 574 |
+
}
|
| 575 |
+
} catch (e) {
|
| 576 |
+
console.warn('OAuth redirect handling failed:', e);
|
| 577 |
+
}
|
| 578 |
+
|
| 579 |
+
function getToken() {
|
| 580 |
+
return sessionStorage.getItem(TOKEN_KEY);
|
| 581 |
+
}
|
| 582 |
+
function clearToken() {
|
| 583 |
+
sessionStorage.removeItem(TOKEN_KEY);
|
| 584 |
+
}
|
| 585 |
+
async function startLogin() {
|
| 586 |
+
try {
|
| 587 |
+
const url = await oauthLoginUrl({ scopes: 'openid profile read-repos' });
|
| 588 |
+
// Preserve the user's current path so they come back here after auth
|
| 589 |
+
window.location.href = url + '&prompt=consent';
|
| 590 |
+
} catch (e) {
|
| 591 |
+
alert('Could not start sign-in: ' + (e.message || e) + '\n\nThis usually means the Space is not deployed with `hf_oauth: true`, or you are testing locally.');
|
| 592 |
+
}
|
| 593 |
+
}
|
| 594 |
|
| 595 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 596 |
// STATE
|
|
|
|
| 740 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 741 |
// FETCH
|
| 742 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 743 |
+
function authHeaders() {
|
| 744 |
+
const token = getToken();
|
| 745 |
+
return token ? { Authorization: `Bearer ${token}` } : {};
|
| 746 |
+
}
|
| 747 |
+
|
| 748 |
async function fetchTree() {
|
| 749 |
+
const resp = await fetch(TREE_URL, { headers: authHeaders() });
|
| 750 |
if (!resp.ok) {
|
| 751 |
const err = new Error(`HTTP ${resp.status}`);
|
| 752 |
err.status = resp.status;
|
|
|
|
| 756 |
}
|
| 757 |
|
| 758 |
async function fetchFile(path) {
|
| 759 |
+
const resp = await fetch(RESOLVE_BASE + path, { headers: authHeaders() });
|
| 760 |
+
if (!resp.ok) {
|
| 761 |
+
const err = new Error(`HTTP ${resp.status} for ${path}`);
|
| 762 |
+
err.status = resp.status;
|
| 763 |
+
throw err;
|
| 764 |
+
}
|
| 765 |
return resp.text();
|
| 766 |
}
|
| 767 |
|
|
|
|
| 973 |
// ERROR / EMPTY STATES
|
| 974 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 975 |
function showAuthError() {
|
| 976 |
+
setLiveStatus(false, 'SIGN IN');
|
| 977 |
+
clearToken();
|
| 978 |
messagesEl.innerHTML = `
|
| 979 |
<div class="state-screen">
|
| 980 |
+
<div class="icon">π€</div>
|
| 981 |
+
<h2>Sign in to view the chat</h2>
|
| 982 |
<p>
|
| 983 |
+
This Space reads messages from the private
|
| 984 |
+
<a href="https://huggingface.co/buckets/${BUCKET}/tree/${PREFIX}" target="_blank">parameter-golf-collab bucket</a>.
|
| 985 |
+
Sign in with a Hugging Face account that has access to <code>ml-agent-explorers</code> to load the messages.
|
|
|
|
| 986 |
</p>
|
| 987 |
+
<button id="loginBtn">Sign in with Hugging Face</button>
|
| 988 |
</div>
|
| 989 |
`;
|
| 990 |
+
document.getElementById('loginBtn').addEventListener('click', startLogin);
|
| 991 |
}
|
| 992 |
function showFetchError(err) {
|
| 993 |
setLiveStatus(false, 'OFFLINE');
|
|
|
|
| 1009 |
// INITIAL LOAD + POLL
|
| 1010 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 1011 |
async function initialLoad() {
|
| 1012 |
+
// No token yet β straight to the sign-in screen, don't even try fetching.
|
| 1013 |
+
if (!getToken()) {
|
| 1014 |
+
loadingScreen?.remove();
|
| 1015 |
+
showAuthError();
|
| 1016 |
+
return;
|
| 1017 |
+
}
|
| 1018 |
try {
|
| 1019 |
const fresh = await fetchAllMessages();
|
| 1020 |
loadingScreen?.remove();
|
|
|
|
| 1036 |
initialLoaded = true;
|
| 1037 |
setLiveStatus(true, 'LIVE');
|
| 1038 |
} catch (err) {
|
| 1039 |
+
loadingScreen?.remove();
|
| 1040 |
if (err.status === 401 || err.status === 403) showAuthError();
|
| 1041 |
else showFetchError(err);
|
| 1042 |
}
|