ui stuff, proxy?, and whatever else

This commit is contained in:
first@lynx.com
2026-08-17 14:23:34 -04:00
parent b1a1403c13
commit 9d640ff608
18 changed files with 1262 additions and 149 deletions

View File

@ -6,7 +6,7 @@
<script async src="https://www.googletagmanager.com/gtag/js?id=G-J0Y3T4HN4N"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
function gtag() { dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', 'G-J0Y3T4HN4N');
@ -84,9 +84,19 @@
if (localStorage.getItem("selenite.super-fast-mode") == 'true') {
document.getElementById("superFastMode").setAttribute("checked", "")
};
if (localStorage.getItem("selenite.disableTopGames") == 'false') {
if (localStorage.getItem("selenite.disableTopGames") == 'true') {
document.getElementById("topGamesToggle").setAttribute("checked", "")
};
if (localStorage.getItem("selenite.disableRecentlyPlayed") == 'true') {
document.getElementById("recentlyPlayedToggle").setAttribute("checked", "")
};
try {
const savedPanic = JSON.parse(localStorage.getItem("selenite.panic-mode") || "null");
if (savedPanic && savedPanic.key && savedPanic.url) {
document.getElementById("panicKey").value = savedPanic.key;
document.getElementById("panicURL").value = savedPanic.url;
}
} catch { }
document.getElementById("themes").addEventListener("input", (e) => {
localStorage.setItem("selenite.theme", e.target.value);
document.body.setAttribute("theme", e.target.value);
@ -114,26 +124,93 @@
location.reload();
});
document.getElementById("topGamesToggle").addEventListener("click", (e) => {
localStorage.setItem("selenite.disableTopGames", (e.target.checked == false));
localStorage.setItem("selenite.disableTopGames", e.target.checked ? "true" : "false");
});
document.getElementById("submitCloak").addEventListener("click", (e) => {
if (document.getElementById("cloaks").value) {
localStorage.setItem("selenite.tab-cloak", JSON.stringify({
"name": cloaks[document.getElementById("cloaks").value][0],
"icon": cloaks[document.getElementById("cloaks").value][1]
}));
document.getElementById("recentlyPlayedToggle").addEventListener("click", (e) => {
localStorage.setItem("selenite.disableRecentlyPlayed", e.target.checked ? "true" : "false");
});
document.getElementById("submitCloak").addEventListener("click", () => {
const feedback = document.getElementById("cloakFeedback");
const presetValue = document.getElementById("cloaks").value;
let name, icon;
if (presetValue) {
name = cloaks[presetValue][0];
icon = cloaks[presetValue][1];
} else {
localStorage.setItem("selenite.tab-cloak", JSON.stringify({
"name": document.getElementById("cloakName").value || "",
"icon": document.getElementById("cloakIcon").value || ""
}));
name = document.getElementById("cloakName").value.trim();
icon = document.getElementById("cloakIcon").value.trim();
}
localStorage.setItem("selenite.tab-cloak", JSON.stringify({ name, icon }));
if (!name && !icon) {
feedback.textContent = "tab cloak reset to default.";
feedback.className = "feedback";
} else {
feedback.textContent = `tab cloak set to "${name || "Selenite"}"!`;
feedback.className = "feedback success";
}
});
document.getElementById("submitPanic").addEventListener("click", (e) => {
localStorage.setItem("selenite.panic-mode", JSON.stringify({
"key": document.getElementById("panicKey").value || "",
"url": document.getElementById("panicURL").value || ""
}));
document.getElementById("submitPanic").addEventListener("click", () => {
const feedback = document.getElementById("panicFeedback");
const key = document.getElementById("panicKey").value.trim();
let url = document.getElementById("panicURL").value.trim();
if (!key) {
feedback.textContent = "enter a key to trigger panic mode.";
feedback.className = "feedback error";
return;
}
if (!url) {
feedback.textContent = "enter a website to redirect to.";
feedback.className = "feedback error";
return;
}
if (!/^https?:\/\//i.test(url)) {
url = "https://" + url;
}
try {
new URL(url);
} catch {
feedback.textContent = "that doesn't look like a valid website url.";
feedback.className = "feedback error";
return;
}
localStorage.setItem("selenite.panic-mode", JSON.stringify({ key, url }));
document.getElementById("panicURL").value = url;
feedback.textContent = `panic mode saved! press "${key}" to instantly leave.`;
feedback.className = "feedback success";
});
document.getElementById("submitProxyUnlock").addEventListener("click", () => {
const feedback = document.getElementById("proxyUnlockFeedback");
const passwordInput = document.getElementById("proxyPassword");
const password = passwordInput.value;
if (!password) {
feedback.textContent = "enter a password.";
feedback.className = "feedback error";
return;
}
fetch("/api/proxy/unlock", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ password }),
})
.then((res) => res.json())
.then((data) => {
if (data.success) {
feedback.textContent = "beta access granted!";
feedback.className = "feedback success";
passwordInput.value = "";
} else {
feedback.textContent = data.message || "incorrect password.";
feedback.className = "feedback error";
}
})
.catch(() => {
feedback.textContent = "failed to reach the server.";
feedback.className = "feedback error";
});
});
document.getElementById("toggleCDN").addEventListener("change", () => {
let toggle = document.getElementById("toggleCDN");
@ -207,10 +284,14 @@
updateHiddenGamesCount(games.length);
});
const box = document.createElement("span");
box.className = "checkboxBox";
const text = document.createElement("span");
text.innerText = game.name;
row.appendChild(checkbox);
row.appendChild(box);
row.appendChild(text);
list.appendChild(row);
});
@ -234,6 +315,27 @@
searchInput.addEventListener("input", () => {
renderHiddenGameList(games, new Set(getHiddenGames()), searchInput.value);
});
document.getElementById("unhideAllGames").addEventListener("click", () => {
const feedback = document.getElementById("hiddenGamesFeedback");
const previousCount = getHiddenGames().length;
setHiddenGames([]);
renderHiddenGameList(games, new Set(), searchInput.value);
updateHiddenGamesCount(games.length);
if (feedback) {
if (previousCount > 0) {
feedback.textContent = `unhid ${previousCount} game${previousCount === 1 ? "" : "s"}!`;
feedback.className = "feedback success";
} else {
feedback.textContent = "no games were hidden.";
feedback.className = "feedback";
}
}
});
}
function clearRecentlyPlayed() {
localStorage.removeItem("selenite.recentlyPlayed");
}
function wipeData() {
@ -244,38 +346,85 @@
})
}
}
function setDataFeedback(message, type) {
const el = document.getElementById("dataFeedback");
if (!el) return;
el.textContent = message;
el.className = "feedback" + (type ? " " + type : "");
}
function downloadData() {
let storage = JSON.stringify(localStorage);
let cookies = document.cookie;
let data = btoa(JSON.stringify({ localStorage: storage, cookies: cookies }));
let file = new Blob([data], { type: "text" });
let a = document.createElement("a");
a.href = URL.createObjectURL(file);
a.download = "selenite.dat";
a.click();
try {
const data = JSON.stringify({
localStorage: { ...localStorage },
cookies: document.cookie
});
const file = new Blob([data], { type: "application/json" });
const url = URL.createObjectURL(file);
const a = document.createElement("a");
a.href = url;
a.download = "selenite.dat";
a.click();
URL.revokeObjectURL(url);
setDataFeedback("data downloaded!", "success");
} catch (err) {
setDataFeedback("failed to download data: " + err.message, "error");
}
}
function parseBackup(text) {
let data;
try {
data = JSON.parse(text);
} catch {
// fall back to the old base64 + double-stringified-localStorage format
data = JSON.parse(atob(text));
}
if (!data || typeof data.cookies !== "string") {
throw new Error("this file doesn't look like a selenite backup.");
}
let local = data.localStorage;
if (typeof local === "string") local = JSON.parse(local);
if (!local || typeof local !== "object") {
throw new Error("this file doesn't look like a selenite backup.");
}
return { localStorage: local, cookies: data.cookies };
}
function uploadData() {
let input = document.createElement("input");
const input = document.createElement("input");
input.type = "file";
input.accept = "dat";
input.click();
input.accept = ".dat,application/json";
input.addEventListener("change", e => {
if (prompt("Are you sure you want to upload data?\nThis may overwrite any previous data you had stored.\n\nType \"i am sure\" to continue.") == "i am sure") {
let fileReader = new FileReader();
fileReader.addEventListener("load", e => {
let data = JSON.parse(atob(e.target.result));
let local = JSON.parse(data["localStorage"]);
let cookies = data["cookies"];
const file = e.target.files[0];
if (!file) return;
if (prompt("Are you sure you want to upload data?\nThis may overwrite any previous data you had stored.\n\nType \"i am sure\" to continue.") != "i am sure") return;
document.cookie = cookies;
Object.keys(local).forEach(e => {
localStorage.setItem(e, local[e]);
})
});
fileReader.readAsText(e.target.files[0]);
}
})
const fileReader = new FileReader();
fileReader.addEventListener("load", e => {
try {
const { localStorage: local, cookies } = parseBackup(e.target.result);
document.cookie.split(";").forEach(c => {
const name = c.split("=")[0].trim();
if (name) document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT";
});
cookies.split(";").forEach(c => {
c = c.trim();
if (c) document.cookie = c;
});
Object.keys(local).forEach(key => {
localStorage.setItem(key, local[key]);
});
setDataFeedback("data uploaded! reload the page to see changes.", "success");
} catch (err) {
setDataFeedback("failed to upload data: " + err.message, "error");
}
});
fileReader.addEventListener("error", () => {
setDataFeedback("failed to read that file.", "error");
});
fileReader.readAsText(file);
});
input.click();
};
async function deleteAllCaches() {
try {
@ -308,12 +457,18 @@
</select>
<input type="color" id="colorPicker" style="display: none">
<p style="font-size:20px;margin:6px;">toggles</p>
<div class="samerow"><input type="checkbox" id="fastMode"><label for="fastMode"
<div class="toggleRow"><label class="switch"><input type="checkbox" id="fastMode"><span
class="switchTrack"></span></label><label for="fastMode"
title="disables particles, animated background, & other misc effects">fast mode</label></div>
<div class="samerow"><input type="checkbox" id="superFastMode"><label for="superFastMode"
<div class="toggleRow"><label class="switch"><input type="checkbox" id="superFastMode"><span
class="switchTrack"></span></label><label for="superFastMode"
title="disables all blurs and transparency">super fast mode</label></div>
<div class="samerow"><input type="checkbox" id="topGamesToggle"><label for="topGamesToggle">disable top
<div class="toggleRow"><label class="switch"><input type="checkbox" id="topGamesToggle"><span
class="switchTrack"></span></label><label for="topGamesToggle">disable top
games</label></div>
<div class="toggleRow"><label class="switch"><input type="checkbox" id="recentlyPlayedToggle"><span
class="switchTrack"></span></label><label for="recentlyPlayedToggle">disable
recently played</label></div>
</section>
<section>
<h2>tab cloak</h2>
@ -332,6 +487,7 @@
</select>
<button id="submitCloak">set tab cloak</button>
<p id="cloakFeedback" class="feedback"></p>
</section>
<section>
<h2>panic mode</h2>
@ -340,6 +496,7 @@
<p title="the url itll direct you to">website</p>
<input type="text" id="panicURL">
<button id="submitPanic">set panic mode</button>
<p id="panicFeedback" class="feedback"></p>
</section>
<section>
<h2>manage data</h2>
@ -347,13 +504,29 @@
<button onclick="uploadData()">upload</button>
<button onclick="wipeData()">wipe (erases all game data)</button>
<button onclick="deleteAllCaches()">wipe all cache</button>
<input id="toggleCDN" name="toggleCDN" type="checkbox"></inpuit> <label for="toggleCDN">cdn enabled</label>
<button onclick="clearRecentlyPlayed()">clear recently played</button>
<div class="toggleRow"><label class="switch"><input id="toggleCDN" name="toggleCDN" type="checkbox"><span
class="switchTrack"></span></label><label for="toggleCDN">cdn enabled</label></div>
<p id="dataFeedback" class="feedback"></p>
</section>
<section>
<section id="hiddenGamesSection">
<h2>hidden games</h2>
<p id="hiddenGamesCount">0 hidden</p>
<input type="text" id="hiddenGamesSearch" placeholder="search games">
<div id="hiddenGamesList"></div>
<div class="hiddenGamesRow">
<div class="hiddenGamesControls">
<input type="text" id="hiddenGamesSearch" placeholder="search games">
<button id="unhideAllGames">unhide all</button>
<p id="hiddenGamesFeedback" class="feedback"></p>
</div>
<div id="hiddenGamesList"></div>
</div>
</section>
<section>
<h2>beta access</h2>
<p title="the secret password for beta access">password</p>
<input type="password" id="proxyPassword" autocomplete="off">
<button id="submitProxyUnlock">unlock</button>
<p id="proxyUnlockFeedback" class="feedback"></p>
</section>
</sections>
</body>