This commit is contained in:
wearrrrr 2024-02-08 09:28:15 -06:00
commit 94f210f7f7
41 changed files with 14544 additions and 12454 deletions

2
.prettierignore Normal file
View file

@ -0,0 +1,2 @@
dist
node_modules

View file

@ -1,4 +1,4 @@
import { defineConfig } from 'astro/config';
import { defineConfig } from "astro/config";
import node from "@astrojs/node";
@ -8,5 +8,5 @@ export default defineConfig({
output: "hybrid",
adapter: node({
mode: "middleware",
})
});
}),
});

108
index.js
View file

@ -1,56 +1,96 @@
import { createBareServer } from "@tomphttp/bare-server-node"
import { uvPath } from "@nebula-services/ultraviolet"
import http from 'node:http';
import path from 'node:path';
import express from 'express';
import { handler as ssrHandler } from './dist/server/entry.mjs';
import dotenv from 'dotenv';
import compression from "compression"
import chalk from "chalk"
import { uvPath } from "@nebula-services/ultraviolet";
import { createBareServer } from "@tomphttp/bare-server-node";
import express from "express";
import { createServer } from "http";
import path from "node:path";
import createRammerhead from "rammerhead/src/server/index.js";
import compression from "compression";
import { build } from "astro";
import chalk from "chalk";
import { existsSync } from "fs";
import dotenv from "dotenv";
dotenv.config();
if (!existsSync("./dist")) build();
const PORT = process.env.PORT || 3000;
const server = http.createServer();
const app = express(server);
const bareServer = createBareServer("/bare/");
app.use(compression());
app.use(express.static(path.join(process.cwd(), "static")));
app.use(express.static(path.join(process.cwd(), "build")));
app.use("/uv/", express.static(uvPath));
app.use(express.json());
app.use(express.urlencoded({
extended: true
})
);
app.use("/", express.static('dist/client/'));
app.use(ssrHandler);
const bare = createBareServer("/bare/");
app.get('*', function(req, res){
res.status(200).sendFile("404.html", {root: path.resolve("dist/client")});
});
const rh = createRammerhead();
const rammerheadScopes = [
"/rammerhead.js",
"/hammerhead.js",
"/transport-worker.js",
"/task.js",
"/iframe-task.js",
"/worker-hammerhead.js",
"/messaging",
"/sessionexists",
"/deletesession",
"/newsession",
"/editsession",
"/needpassword",
"/syncLocalStorage",
"/api/shuffleDict",
"/mainport",
];
const rammerheadSession = /^\/[a-z0-9]{32}/;
function shouldRouteRh(req) {
const url = new URL(req.url, "http://0.0.0.0");
return rammerheadScopes.includes(url.pathname) || rammerheadSession.test(url.pathname);
}
function routeRhRequest(req, res) {
rh.emit("request", req, res);
}
function routeRhUpgrade(req, socket, head) {
rh.emit("upgrade", req, socket, head);
}
let server = createServer();
server.on("request", (req, res) => {
if (bareServer.shouldRoute(req)) {
bareServer.routeRequest(req, res);
if (bare.shouldRoute(req)) {
bare.routeRequest(req, res);
} else if (shouldRouteRh(req)) {
routeRhRequest(req, res);
} else {
app(req, res);
}
});
server.on("upgrade", (req, socket, head) => {
if (bareServer.shouldRoute(req)) {
bareServer.routeUpgrade(req, socket, head);
if (bare.shouldRoute(req)) {
bare.routeUpgrade(req, socket, head);
} else if (shouldRouteRh(req)) {
routeRhUpgrade(req, socket, head);
} else {
socket.end();
}
});
console.log(chalk.gray("Starting Alu..."))
const app = express();
app.use(compression());
app.use(express.static(path.join(process.cwd(), "static")));
app.use(express.static(path.join(process.cwd(), "build")));
app.use("/uv/", express.static(uvPath));
app.use(express.json());
app.use(
express.urlencoded({
extended: true,
})
);
app.use("/", express.static("dist/client/"));
console.log(chalk.gray("Starting Alu..."));
server.on("listening", () => {
console.log(chalk.green(`Server running at http://localhost:${PORT}/.`));
});
server.listen({
port: PORT
});
port: PORT,
});

58
index.js.bak Normal file
View file

@ -0,0 +1,58 @@
import { createBareServer } from "@tomphttp/bare-server-node";
import { uvPath } from "@nebula-services/ultraviolet";
import http from "node:http";
import path from "node:path";
import express from "express";
import { handler as ssrHandler } from "./dist/server/entry.mjs";
import dotenv from "dotenv";
import compression from "compression";
import createRammerhead from "rammerhead/src/server/index.js";
import chalk from "chalk";
dotenv.config();
const PORT = process.env.PORT || 3000;
const server = http.createServer();
const app = express(server);
const bareServer = createBareServer("/bare/");
app.use(compression());
app.use(express.static(path.join(process.cwd(), "static")));
app.use(express.static(path.join(process.cwd(), "build")));
app.use("/uv/", express.static(uvPath));
app.use(express.json());
app.use(
express.urlencoded({
extended: true,
})
);
app.use("/", express.static("dist/client/"));
app.use(ssrHandler);
app.get("*", function (req, res) {
res.status(200).sendFile("404.html", { root: path.resolve("dist/client") });
});
server.on('request', (req, res) => {
if (bareServer.shouldRoute(req)) {
bareServer.routeRequest(req, res);
} else {
app(req, res);
}
});
server.on('upgrade', (req, socket, head) => {
if (bareServer.shouldRoute(req)) {
bareServer.routeUpgrade(req, socket, head);
} else {
socket.end();
}
});
console.log(chalk.gray("Starting Alu..."));
server.on("listening", () => {
console.log(chalk.green(`Server running at http://localhost:${PORT}/.`));
});
server.listen({
port: PORT,
});

22889
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,27 +1,36 @@
{
"name": "alus-unblocker",
"type": "module",
"version": "0.0.1",
"scripts": {
"start": "node .",
"build": "astro build"
},
"dependencies": {
"@astrojs/check": "^0.4.0",
"@astrojs/node": "^7.0.4",
"@nebula-services/ultraviolet": "^1.0.1-1.patch.5",
"@tomphttp/bare-server-node": "^2.0.1",
"astro": "^4.1.1",
"astro-i18n": "^2.2.4",
"astro-i18next": "^1.0.0-beta.21",
"chalk": "^5.3.0",
"compression": "^1.7.4",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"i": "^0.3.7",
"i18next": "^23.7.18",
"i18next-browser-languagedetector": "^7.2.0",
"npm": "^10.2.5",
"typescript": "^5.3.3"
}
"name": "alus-unblocker",
"type": "module",
"version": "0.0.1",
"scripts": {
"start": "node .",
"build": "astro build",
"lint": "prettier --write .",
"lint:check": "prettier --check ."
},
"dependencies": {
"@astrojs/check": "^0.4.0",
"@astrojs/node": "^7.0.4",
"@nebula-services/ultraviolet": "^1.0.1-1.patch.5",
"@tomphttp/bare-server-node": "^2.0.1",
"astro": "^4.3.3",
"astro-i18n": "^2.2.4",
"astro-i18next": "^1.0.0-beta.21",
"chalk": "^5.3.0",
"compression": "^1.7.4",
"dotenv": "^16.3.1",
"dotenv-flow": "^4.1.0",
"express": "^4.18.2",
"i": "^0.3.7",
"i18next": "^23.7.18",
"i18next-browser-languagedetector": "^7.2.0",
"npm": "^10.2.5",
"path": "^0.12.7",
"rammerhead": "https://github.com/holy-unblocker/rammerhead/releases/download/v1.2.41-holy.5/rammerhead-1.2.41-holy.5.tgz",
"typescript": "^5.3.3"
},
"devDependencies": {
"prettier": "3.2.5",
"prettier-plugin-astro": "^0.13.0"
}
}

12
prettier.config.js Normal file
View file

@ -0,0 +1,12 @@
const config = {
tabWidth: 2,
semi: true,
trailingComma: "es5",
bracketSpacing: true,
bracketSameLine: true,
arrowParens: "always",
plugins: ["prettier-plugin-astro"],
printWidth: 100,
};
export default config;

View file

@ -1,7 +1,7 @@
{
"nav": {
"brand": "Alu",
"games": "Games",
"settings": "Settings"
}
}
"nav": {
"brand": "Alu",
"games": "Games",
"settings": "Settings"
}
}

View file

@ -1,5 +1,5 @@
{
"brand": "アルー",
"games": "ゲーム",
"settings": "設定"
}
"brand": "アルー",
"games": "ゲーム",
"settings": "設定"
}

View file

@ -5,14 +5,14 @@
* Ideally, this will be registered under the scope in uv.config.js so it will not need to be modified.
* However, if a user changes the location of uv.bundle.js/uv.config.js or sw.js is not relative to them, they will need to modify this script locally.
*/
importScripts('/uv/uv.bundle.js');
importScripts('/uv.config.js');
importScripts("/uv/uv.bundle.js");
importScripts("/uv.config.js");
importScripts(__uv$config.sw);
self.addEventListener("install", (event) => {
self.skipWaiting();
self.skipWaiting();
});
const sw = new UVServiceWorker();
self.addEventListener('fetch', (event) => event.respondWith(sw.fetch(event)));
self.addEventListener("fetch", (event) => event.respondWith(sw.fetch(event)));

View file

@ -1,12 +1,12 @@
/*global Ultraviolet*/
self.__uv$config = {
prefix: '/service/',
bare: '/bare/',
encodeUrl: Ultraviolet.codec.xor.encode,
decodeUrl: Ultraviolet.codec.xor.decode,
handler: '/uv/uv.handler.js',
client: '/uv/uv.client.js',
bundle: '/uv/uv.bundle.js',
config: '/uv/uv.config.js',
sw: '/uv/uv.sw.js',
};
prefix: "/service/",
bare: "/bare/",
encodeUrl: Ultraviolet.codec.xor.encode,
decodeUrl: Ultraviolet.codec.xor.decode,
handler: "/uv/uv.handler.js",
client: "/uv/uv.client.js",
bundle: "/uv/uv.bundle.js",
config: "/uv/uv.config.js",
sw: "/uv/uv.sw.js",
};

View file

@ -1,32 +1,32 @@
<script>
function loadCloak() {
let localStorageCloak = localStorage.getItem('alu__selectedCloak');
if (localStorageCloak) {
let parsedCloak = JSON.parse(localStorageCloak);
if (parsedCloak) {
if (parsedCloak.name != "None") {
document.title = parsedCloak.name
let link = document.querySelector("link[rel~='icon']") as HTMLLinkElement;
if (!link) {
link = document.createElement('link');
link.rel = 'icon';
}
link.href = parsedCloak.icon;
document.getElementsByTagName('head')[0].appendChild(link);
}
}
} else {
// Load default cloak
let link = document.querySelector("link[rel~='icon']") as HTMLLinkElement;
if (!link) {
link = document.createElement('link');
link.rel = 'icon';
}
link.href = "/favicon.png";
document.getElementsByTagName('head')[0].appendChild(link);
function loadCloak() {
let localStorageCloak = localStorage.getItem("alu__selectedCloak");
if (localStorageCloak) {
let parsedCloak = JSON.parse(localStorageCloak);
if (parsedCloak) {
if (parsedCloak.name != "None") {
document.title = parsedCloak.name;
let link = document.querySelector("link[rel~='icon']") as HTMLLinkElement;
if (!link) {
link = document.createElement("link");
link.rel = "icon";
}
link.href = parsedCloak.icon;
document.getElementsByTagName("head")[0].appendChild(link);
}
}
} else {
// Load default cloak
let link = document.querySelector("link[rel~='icon']") as HTMLLinkElement;
if (!link) {
link = document.createElement("link");
link.rel = "icon";
}
link.href = "/favicon.png";
document.getElementsByTagName("head")[0].appendChild(link);
}
}
loadCloak();
document.addEventListener("astro:after-swap", loadCloak);
</script>
loadCloak();
document.addEventListener("astro:after-swap", loadCloak);
</script>

View file

@ -1,61 +1,65 @@
---
const { buttonNameDefault, dropdownList, id } = Astro.props;
---
<div class="dropdown">
<button id={id} class="dropdown-toggle" type="button" data-toggle="dropdown">
{buttonNameDefault}
<span class="caret"></span></button>
<ul class="dropdown-menu" id={id + "-menu"}>
<button id={id} class="dropdown-toggle" type="button" data-toggle="dropdown">
{buttonNameDefault}
<span class="caret"></span></button
>
<ul class="dropdown-menu" id={id + "-menu"}>
{
dropdownList.map((item: any) => {
return <li class="dropdown-item" data-setting={item.value}>{item.name}</li>
})
dropdownList.map((item: any) => {
return (
<li class="dropdown-item" data-setting={item.value}>
{item.name}
</li>
);
})
}
</ul>
</ul>
</div>
<style>
.dropdown {
box-shadow: 4px 6px 15px 0px var(--background-color);
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
position: relative;
}
.dropdown-toggle {
background-color: var(--accent-color);
border: none;
color: var(--text-color);
padding: 12px 16px;
font-size: 16px;
cursor: pointer;
font-family: 'Varela Round', sans-serif;
width: 100%;
border-radius: 10px;
min-width: 140px;
height: 50px;
}
.dropdown-menu {
margin: 0;
list-style: none;
padding: 0;
text-align: center;
max-height: 0px; /* Set max-height to 0 instead of height */
overflow: hidden;
transition: max-height 350ms ease-in-out; /* Use max-height in the transition property */
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
background-color: var(--dropdown-background-color);
position: absolute;
width: 100%;
}
.dropdown-item {
border-bottom: 1px solid var(--text-color-accent);
padding: 10px;
color: var(--text-color);
cursor: pointer;
}
.dropdown-item:nth-last-child(1) {
border-bottom: none;
}
</style>
.dropdown {
box-shadow: 4px 6px 15px 0px var(--background-color);
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
position: relative;
}
.dropdown-toggle {
background-color: var(--accent-color);
border: none;
color: var(--text-color);
padding: 12px 16px;
font-size: 16px;
cursor: pointer;
font-family: "Varela Round", sans-serif;
width: 100%;
border-radius: 10px;
min-width: 140px;
height: 50px;
}
.dropdown-menu {
margin: 0;
list-style: none;
padding: 0;
text-align: center;
max-height: 0px; /* Set max-height to 0 instead of height */
overflow: hidden;
transition: max-height 350ms ease-in-out; /* Use max-height in the transition property */
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
background-color: var(--dropdown-background-color);
position: absolute;
width: 100%;
}
.dropdown-item {
border-bottom: 1px solid var(--text-color-accent);
padding: 10px;
color: var(--text-color);
cursor: pointer;
}
.dropdown-item:nth-last-child(1) {
border-bottom: none;
}
</style>

View file

@ -1,139 +1,66 @@
---
import { getLangFromUrl, useTranslations } from "../i18n/utils"
import { getLangFromUrl, useTranslations } from "../i18n/utils";
import WaveSVG from "./WaveSVG.astro";
const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
---
<div id="footer">
<div class="footerflex">
<div class="footerbrand">
<p class="footerlist-heading"><a href="/">{t("footer.brand")}</a></p>
<p><a target="_blank" rel="noopener noreferrer" href="https://wearr.dev">{t("footer.madeWithLove")}</a></p>
</div>
<div class="footerlist">
<p class="footerlist-heading">{t("footer.services")}</p>
<ul>
<li><a target="_blank" rel="noopener noreferrer" href="https://github.com/titaniumnetwork-dev/Ultraviolet">Ultraviolet</a></li>
</ul>
<div class="footer">
<div class="wave-svg-container">
<WaveSVG />
</div>
<div class="footer-top">
<div class="footer-title">
<h2>{t("footer.brand")}</h2>
</div>
<div class="footerlist">
<p class="footerlist-heading">{t("footer.socials")}</p>
<ul>
<li><a target="_blank" rel="noopener noreferrer" href="https://github.com/wearrrrr/AlusUnblocker">GitHub</a></li>
<li><a target="_blank" rel="noopener noreferrer" href="#">Discord</a></li>
</ul>
<div class="footer-madeby">
<a href="https://wearr.dev">{t("footer.madeWithLove")}</a>
</div>
</div>
<p class="copyright">{t("footer.aluProject")} &copy; 2024</p>
</div>
</div>
<div class="footer-bottom">
<div class="footer-links">
<a href="https://titaniumnetwork.org">{t("footer.poweredBy")}</a>
</div>
<div class="footer-copyright">
<p>&copy; {t("footer.aluProject")} 2024</p>
</div>
</div>
</div>
<style>
:root {
--footer-svg-url: url("/img/aluwaves.svg");
}
[data-theme="mocha"] {
--footer-svg-url: url("/img/mochawaves.svg");
}
[data-theme="macchiato"] {
--footer-svg-url: url("/img/macchiatowaves.svg");
}
#footer {
padding-top: 22vh;
background-image: var(--footer-svg-url);
background-repeat: no-repeat;
background-size: 100vw auto;
.wave-svg-container {
bottom: 0;
left: 0;
position: absolute;
width: 100%;
}
z-index: -1;
overflow: hidden;
height: 300px;
filter: brightness(1.5);
}
.footerflex {
display: -webkit-box;
display: -ms-flexbox;
.footer {
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
}
flex-direction: column;
justify-content: space-around;
color: var(--text-color);
position: relative;
padding-top: 100px;
gap: 50px;
}
.footer-top,
.footer-bottom {
width: 90%;
margin: 0 auto;
}
.footersocials {
text-align: center;
}
.footerflex > div {
margin: 25px 50px;
}
@media only screen and (max-width: 600px) {
.footerflex > div {
margin: 15px 25px;
}
}
@media only screen and (max-width: 380px) {
.footerflex > div {
margin: 5px 10px;
}
}
.footerbrand div a {
font-family: 'Varela Round', sans-serif;
color: var(--text-color) !important;
.footer-title {
font-size: 20px;
font-weight: bold;
}
}
.footerbrand p {
font-family: 'Varela Round', sans-serif;
a {
color: var(--text-color);
}
.footerbrand a {
font-family: 'Varela Round', sans-serif;
color: var(--text-color);
}
.footerlist-heading {
font-weight: bold;
font-size: 20px;
}
.footerlist ul {
list-style: none;
padding: 0;
}
.footerlist ul > li {
padding: 2px;
padding-bottom: 5px;
}
.footerlist ul > li > a {
color: var(--text-color);
}
.footerlist > * {
font-family: 'Varela Round', sans-serif;
color: var(--text-color);
}
.footersocials {
font-size: 20px;
}
.footersocials a {
display: inline-block;
width: 20px;
height: 20px;
line-height: 20px;
padding: 6px;
margin: 0 5px;
border-radius: 50%;
}
.copyright {
color: #D8DEE9;
text-align: center;
}
</style>
}
</style>

View file

@ -3,27 +3,30 @@ const { name, image, slugName } = Astro.props;
---
<div class="game">
<a href=`/games/${slugName || name}`><img class="game-img" src=`${image}` alt=""></a>
<p class="game-title">{name}</p>
<a href=`/games/${slugName || name}`>
<img class="game-img" src={image} alt="" />
</a>
<p class="game-title">{name}</p>
</div>
<style>
.game {
border: 2px solid #D8DEE9;
.game {
border: 2px solid var(--text-color);
padding: 1rem;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.game-img {
border-radius: 15px;
}
.game-img {
width: 128px;
height: 128px;
}
}
.game-title {
.game-title {
color: var(--text-color);
text-align: center;
max-width: 125px;
}
</style>
}
</style>

View file

@ -1,29 +1,29 @@
---
import { getLangFromUrl, useTranslations } from "../i18n/utils"
const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
import { getLangFromUrl, useTranslations } from "../i18n/utils";
const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
---
<div class="top-header">
<div id="title-background" class="title-background">
<div class="left">
<a href={`/${lang}/`} class="header-item">{t("nav.brand")}</a>
</div>
<div class="right">
<a href={`/${lang}/games/`} class="header-item">{t("nav.games")}</a>
<a href={`/${lang}/settings/`} class="header-item">{t("nav.settings")}</a>
</div>
<div id="title-background" class="title-background">
<div class="left">
<a href={`/${lang}/`} class="header-item">{t("nav.brand")}</a>
</div>
<div class="right">
<a href={`/${lang}/games/`} class="header-item">{t("nav.games")}</a>
<a href={`/${lang}/settings/`} class="header-item">{t("nav.settings")}</a>
</div>
</div>
</div>
<style>
.top-header {
.top-header {
display: flex;
box-sizing: border-box;
transition: 250ms ease-in;
}
}
.title-background {
.title-background {
background-color: var(--accent-color);
display: flex;
align-items: center;
@ -37,28 +37,29 @@
z-index: 10;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
}
.left, .right {
.left,
.right {
display: flex;
align-items: center;
gap: 20px;
}
}
.header-item {
.header-item {
color: var(--text-color);
text-decoration: none;
font-size: 22px;
transition: 250ms ease-in-out;
}
}
.header-item:hover {
.header-item:hover {
color: var(--text-color-accent);
}
}
@media only screen and (max-width: 1003px) {
@media only screen and (max-width: 1003px) {
.title-background {
width: 500%;
width: 500%;
}
}
</style>
}
</style>

View file

@ -1,31 +1,33 @@
---
const { type, inputName, defaultTextContent, height, placeholder } = Astro.props;
// typescript brainrot
type HTMLInputTypeAttribute = "button" | "checkbox" | "color" | "date" | "datetime-local" | "email" | "file" | "hidden" | "image" | "month" | "number" | "password" | "radio" | "range" | "reset" | "search" | "submit" | "tel" | "text" | "time" | "url" | "week";
const { inputName, defaultTextContent, height, placeholder } = Astro.props;
interface Props {
type?: HTMLInputTypeAttribute;
inputName: string;
defaultTextContent?: string;
height: string;
placeholder?: string;
inputName: string;
defaultTextContent?: string;
height: string;
placeholder?: string;
}
---
<input style={`height: ${height}`} id={inputName + "-input"} placeholder={placeholder || ""} value={defaultTextContent || ""} type={type} />
<input
style={`height: ${height}`}
id={inputName + "-input"}
placeholder={placeholder || ""}
value={defaultTextContent || ""}
type="text"
/>
<style>
input {
width: 100%;
border: 2px solid var(--accent-color-brighter);
border-radius: 10px;
background-color: #1b1b1b;
color: var(--text-color);
transition: .1s ease-in-out;
}
input:focus {
outline: none;
border: 2px solid var(--text-color);
}
</style>
input {
width: 100%;
border: 2px solid var(--background-highlight);
border-radius: 10px;
background-color: var(--accent-color);
color: var(--text-color);
transition: 0.1s ease-in-out;
}
input:focus {
outline: none;
border: 2px solid var(--text-color);
}
</style>

View file

@ -0,0 +1,136 @@
<script src="/uv/uv.bundle.js" transition:persist></script>
<script src="/uv.config.js" transition:persist></script>
<script>
//@ts-nocheck
let form = document.querySelector("form");
let input = document.querySelector("input");
window.navigator.serviceWorker.register("/sw.js", {
scope: window.__uv$config.prefix,
});
document.addEventListener("astro:after-swap", initForm);
function initForm() {
let formEle = document.querySelector("form");
input = document.querySelector("input");
if (formEle) formEle.addEventListener("submit", formEventListener);
}
if (form) {
form.addEventListener("submit", formEventListener);
}
async function formEventListener(event) {
event.preventDefault();
let loadingContent = document.getElementById("loading-content");
loadingContent.style.opacity = 1;
let url = input.value.trim();
if (!isUrl(url)) url = getSearchEngine() + url;
else if (!(url.startsWith("https://") || url.startsWith("http://"))) url = "http://" + url;
let iframe = document.getElementById("proxy-frame");
let preference = getProxyPreference();
if (preference === "ultraviolet") {
iframe.src = window.__uv$config.prefix + window.__uv$config.encodeUrl(url);
} else if (preference == "rammerhead") {
// Check if rammerhead-session exists in cookies
let rammerheadSession = getCookie("rammerhead-session");
console.log(rammerheadSession);
if (!rammerheadSession) {
let session = await fetch("/newsession");
let sessionID = await session.text();
// Now save it in a cookie that expires in 72 hours.
document.cookie = `rammerhead-session=${sessionID}; max-age=${60 * 60 * 72}; path=/`;
}
iframe.src = `/${getCookie("rammerhead-session")}/${url}`;
} else {
// Default to UV
iframe.src = window.__uv$config.prefix + window.__uv$config.encodeUrl(url);
}
iframe.style.pointerEvents = "auto";
iframe.classList.add("proxy-frame");
document.body.appendChild(iframe);
iframe.addEventListener("load", () => {
let topBar = document.getElementById("top-bar");
loadingContent.style.opacity = 0;
topBar.innerHTML = "";
topBar.classList.add("top-bar");
let closeButton = document.createElement("button");
closeButton.classList.add("close-button");
closeButton.innerText = "Close";
closeButton.addEventListener("click", () => {
iframe.style.opacity = 0;
topBar.style.opacity = 0;
iframe.style.pointerEvents = "none";
topBar.style.pointerEvents = "none";
});
let urlText = document.createElement("p");
urlText.classList.add("url-text");
urlText.innerText = window.__uv$config.decodeUrl(iframe.src.split(__uv$config.prefix)[1]);
iframe.style.opacity = 1;
topBar.style.opacity = 1;
topBar.style.pointerEvents = "auto";
topBar.appendChild(closeButton);
topBar.appendChild(urlText);
document.body.appendChild(topBar);
});
}
function isUrl(val = "") {
if (/^http(s?):\/\//.test(val) || (val.includes(".") && val.substr(0, 1) !== " ")) return true;
return false;
}
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(";").shift();
}
function getSearchEngine() {
let localStorageSearchEngine = localStorage.getItem("alu__search_engine");
if (!localStorageSearchEngine) {
return "https://google.com/search?q=";
}
switch (JSON.parse(localStorage.getItem("alu__search_engine")).value.toLowerCase()) {
case "google": {
return "https://google.com/search?q=";
}
case "bing": {
return "https://bing.com/search?q=d";
}
case "brave": {
return "https://search.brave.com/search?q=";
}
case "searx": {
let localStorageURL = localStorage.getItem("alu__searxngUrl");
if (localStorageURL) {
if (localStorageURL == "") return "https://searxng.site/search?q=";
// Ugly hack to remove the trailing slash :)
if (localStorageURL.endsWith("/")) localStorageURL = localStorageURL.slice(0, -1);
return localStorageURL + "/search?q=";
} else return "https://searxng.site/search?q=";
}
default: {
return "https://google.com/search?q=";
}
}
}
function getProxyPreference() {
let localStorageItem = localStorage.getItem("alu__selectedProxy");
if (!localStorageItem) return "uv";
switch (JSON.parse(localStorageItem).value.toLowerCase()) {
case "ultraviolet": {
return "ultraviolet";
}
case "rammerhead":
return "rammerhead";
case "dynamic":
// temporary because dynamic is not implemented yet :)
return "ultraviolet";
default: {
return "uv";
}
}
}
</script>

View file

@ -1,91 +1,118 @@
---
import Input from "../Input.astro"
const presetCloaks = [
{"cloakTitle": "None", "favicon": "/favicon.png"},
{"cloakTitle": "Google", "favicon": "https://t1.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://google.com&size=128"},
{"cloakTitle": "Instructure", "favicon": "https://du11hjcvx0uqb.cloudfront.net/dist/images/favicon-e10d657a73.ico"},
{"cloakTitle": "Google Classroom", "favicon": "https://ssl.gstatic.com/classroom/ic_product_classroom_144.png"},
{"cloakTitle": "Classlink", "favicon": "https://cdn.classlink.com/production/launchpad/resources/images/favicon/favicon-32x32.png"},
{"cloakTitle": "Google Drive", "favicon": "https://ssl.gstatic.com/docs/doclist/images/drive_2022q3_32dp.png"},
{"cloakTitle": "Schoology", "favicon": "https://asset-cdn.schoology.com/sites/all/themes/schoology_theme/favicon.ico"}
]
import Input from "../Input.astro";
const presetCloaks = [
{ cloakTitle: "None", favicon: "/favicon.png" },
{
cloakTitle: "Google",
favicon:
"https://t1.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://google.com&size=128",
},
{
cloakTitle: "Instructure",
favicon: "https://du11hjcvx0uqb.cloudfront.net/dist/images/favicon-e10d657a73.ico",
},
{
cloakTitle: "Google Classroom",
favicon: "https://ssl.gstatic.com/classroom/ic_product_classroom_144.png",
},
{
cloakTitle: "Classlink",
favicon:
"https://cdn.classlink.com/production/launchpad/resources/images/favicon/favicon-32x32.png",
},
{
cloakTitle: "Google Drive",
favicon: "https://ssl.gstatic.com/docs/doclist/images/drive_2022q3_32dp.png",
},
{
cloakTitle: "Schoology",
favicon: "https://asset-cdn.schoology.com/sites/all/themes/schoology_theme/favicon.ico",
},
];
---
<div class="settings-container">
<div class="cloak-container">
<div id="cloak-list">
{presetCloaks.map((cloak: any) => {
return <div class="cloak-item" data-cloak-name={cloak.cloakTitle} data-cloak-icon={cloak.favicon}><img class="cloak-image" src={cloak.favicon} alt={cloak.cloakTitle}/></div>
})}
</div>
<div class="cloak-custom-override">
<Input inputName="cloak-custom-name" placeholder="Custom Name" height="30px" />
<Input inputName="cloak-custom-favicon" placeholder="Custom Favicon" height="30px" />
<button id="cloak-custom-button">Update Cloak</button>
</div>
<div class="cloak-container">
<div id="cloak-list">
{
presetCloaks.map((cloak: any) => {
return (
<div
class="cloak-item"
data-cloak-name={cloak.cloakTitle}
data-cloak-icon={cloak.favicon}>
<img class="cloak-image" src={cloak.favicon} alt={cloak.cloakTitle} />
</div>
);
})
}
</div>
<div class="cloak-custom-override">
<Input inputName="cloak-custom-name" placeholder="Custom Name" height="30px" />
<Input inputName="cloak-custom-favicon" placeholder="Custom Favicon" height="30px" />
<button id="cloak-custom-button">Update Cloak</button>
</div>
</div>
</div>
<style>
.cloak-container {
display: flex;
flex-direction: column;
gap: 10px;
}
#cloak-list {
display: flex;
flex-direction: row;
align-items: center;
gap: 15px;
}
.cloak-item {
width: 75px;
height: 75px;
border-radius: 50%;
padding: 15px;
background-color: var(--dropdown-background-color);
border: 3px solid var(--accent-color);
cursor: pointer;
transition: 250ms ease-in-out;
}
.cloak-item.selected {
/* Make border color brighter */
border: 3px solid var(--accent-color-brighter)
}
.cloak-image {
width: 100%;
height: 100%;
}
.cloak-container {
display: flex;
flex-direction: column;
gap: 10px;
}
#cloak-list {
display: flex;
flex-direction: row;
align-items: center;
gap: 15px;
}
.cloak-item {
width: 75px;
height: 75px;
border-radius: 50%;
padding: 15px;
background-color: var(--dropdown-background-color);
border: 3px solid var(--accent-color);
cursor: pointer;
transition: 250ms ease-in-out;
}
.cloak-item.selected {
/* Make border color brighter */
border: 3px solid var(--accent-color-brighter);
}
.cloak-image {
width: 100%;
height: 100%;
}
.cloak-custom-override {
display: flex;
flex-direction: column;
gap: 4px;
width: 50%;
}
#cloak-custom-button {
margin-top: 6px;
height: 25px;
border-radius: 10px;
background-color: var(--accent-color-brighter);
border: 0;
color: var(--text-color);
cursor: pointer;
}
.cloak-custom-input {
height: 40px;
width: 100%;
border: 2px solid var(--accent-color-brighter);
border-radius: 10px;
background-color: #1b1b1b;
color: var(--text-color);
transition: .1s ease-in-out;
}
.cloak-custom-input:focus {
outline: none;
border: 2px solid var(--text-color);
}
</style>
.cloak-custom-override {
display: flex;
flex-direction: column;
gap: 4px;
width: 50%;
}
#cloak-custom-button {
margin-top: 6px;
height: 25px;
border-radius: 10px;
background-color: var(--accent-color-brighter);
border: 0;
color: var(--text-color);
cursor: pointer;
}
.cloak-custom-input {
height: 40px;
width: 100%;
border: 2px solid var(--accent-color-brighter);
border-radius: 10px;
background-color: #1b1b1b;
color: var(--text-color);
transition: 0.1s ease-in-out;
}
.cloak-custom-input:focus {
outline: none;
border: 2px solid var(--text-color);
}
</style>

View file

@ -1,27 +1,49 @@
---
import { getLangFromUrl, useTranslations } from "../../i18n/utils"
import { getLangFromUrl, useTranslations } from "../../i18n/utils";
const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
---
<div class="settings-container">
<div class="credits-container">
<p class="credit-item">{t("ultraviolet")} - <a href="https://titaniumnetwork.org/">Titanium Network</a></p>
<p class="credit-item">{t("settings.credits.japaneseTranslations")} - <a href="https://wearr.dev">wearr</a></p>
<p class="credit-item">{t("settings.credits.mochaandmacchiatothemes")} - <a href="">Catppuccin</a></p>
</div>
<div class="credits-container">
<p class="credit-item">
{t("ultraviolet")} - <a
target="_blank"
rel="noreferrer noopener"
href="https://titaniumnetwork.org/">Titanium Network</a
>
</p>
<p class="credit-item">
{t("settings.credits.japaneseTranslations")} - <a
target="_blank"
rel="noreferrer noopener"
href="https://wearr.dev">wearr</a
>
</p>
<p class="credit-item">
{t("settings.credits.mochaandmacchiatothemes")} - <a
target="_blank"
rel="noreferrer noopener"
href="https://github.com/catppuccin/catppuccin">Catppuccin</a
>
</p>
<p class="credit-item">
Rosé Pine Theme - <a
target="_blank"
rel="noreferrer noopener"
href="https://rosepinetheme.com/">Rosé Pine</a
>
</p>
</div>
</div>
<style>
.credit-item {
color: var(--text-color);
font-size: 18px;
}
.credit-item > a {
color: var(--text-color);
font-weight: bold;
}
</style>
.credit-item {
color: var(--text-color);
font-size: 18px;
}
.credit-item > a {
color: var(--text-color);
font-weight: bold;
}
</style>

View file

@ -1,29 +1,35 @@
---
import Dropdown from "../Dropdown.astro"
import Dropdown from "../Dropdown.astro";
import { getLangFromUrl, useTranslations } from "../../i18n/utils"
import { getLangFromUrl, useTranslations } from "../../i18n/utils";
const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
const themeList = [
{"name": t("settings.customization.theme.Alu"), "value": "alu"},
{"name": t("settings.customization.theme.Macchiato"), "value": "macchiato"},
{"name": t("settings.customization.theme.Mocha"), "value": "mocha"}
]
{ name: t("settings.customization.theme.Alu"), value: "alu" },
{ name: t("settings.customization.theme.Macchiato"), value: "macchiato" },
{ name: t("settings.customization.theme.Mocha"), value: "mocha" },
{ name: "Rosé Pine", value: "rose_pine" },
];
const languageList = [
{"name": "English", "value": "en"},
{"name": "日本語", "value": "jp"}
]
{ name: "English", value: "en" },
{ name: "日本語", value: "jp" },
];
---
<div class="settings-container">
<div class="setting__theme">
<label class="setting-label">{t("settings.customization.theme")}</label>
<Dropdown buttonNameDefault="Alu" dropdownList={themeList}, id="dropdown__selected-theme" />
</div>
<div class="setting__language">
<label class="setting-label">{t("settings.customization.language")}</label>
<Dropdown buttonNameDefault="English" dropdownList={languageList}, id="dropdown__selected-language" />
</div>
</div>
<div class="setting__theme">
<label class="setting-label">{t("settings.customization.theme")}</label>
<Dropdown buttonNameDefault="Alu" dropdownList={themeList} , id="dropdown__selected-theme" />
</div>
<div class="setting__language">
<label class="setting-label">{t("settings.customization.language")}</label>
<Dropdown
buttonNameDefault="English"
dropdownList={languageList}
,
id="dropdown__selected-language"
/>
</div>
</div>

View file

@ -1,51 +1,66 @@
---
import Input from "../Input.astro"
import Dropdown from "../Dropdown.astro"
import Input from "../Input.astro";
import Dropdown from "../Dropdown.astro";
import { getLangFromUrl, useTranslations } from "../../i18n/utils"
import { getLangFromUrl, useTranslations } from "../../i18n/utils";
const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
const proxyList = [
{ "name": t("settings.proxy.auto"), "value": "auto" },
{ "name": t("ultraviolet"), "value": "ultraviolet" },
{ "name": "Rammerhead", "value": "rammerhead" },
{ "name": "Dynamic", "value": "dynamic" }
]
{ name: t("settings.proxy.auto"), value: "auto" },
{ name: t("ultraviolet"), value: "ultraviolet" },
{ name: "Rammerhead", value: "rammerhead" },
{ name: "Dynamic", value: "dynamic" },
];
const searchEngineList = [
{ "name": "Google", "value": "google" },
{ "name": "Bing", "value": "bing" },
{ "name": "Brave", "value": "brave" },
{ "name": "Searx", "value": "searx" }
]
{ name: "Google", value: "google" },
{ name: "Bing", value: "bing" },
{ name: "Brave", value: "brave" },
{ name: "Searx", value: "searx" },
];
const openPageWith = [
{ "name": t("settings.proxy.openPageWith.embed"), "value": "embed" },
{ "name": "About:Blank", "value": "about:blank" },
{ "name": t("settings.proxy.openPageWith.newTab"), "value": "newTab" },
]
{ name: t("settings.proxy.openPageWith.embed"), value: "embed" },
{ name: "About:Blank", value: "about:blank" },
{ name: t("settings.proxy.openPageWith.newTab"), value: "newTab" },
];
---
<div class="settings-container">
<div class="setting__selected-proxy">
<label class="setting-label">{t("settings.proxy.selectedProxy")}</label>
<Dropdown buttonNameDefault="Ultraviolet" dropdownList={proxyList}, id="dropdown__selected-proxy" />
</div>
<div class="setting__search-engine">
<label class="setting-label">{t("settings.proxy.searchEngine")}</label>
<Dropdown buttonNameDefault="Google" dropdownList={searchEngineList}, id="dropdown__search-engine" />
</div>
<div class="setting__open_with">
<label class="setting-label">{t("settings.proxy.openPageWith")}</label>
<Dropdown buttonNameDefault={t("settings.proxy.openPageWith.embed")} dropdownList={openPageWith}, id="dropdown__open-with" />
</div>
<div class="setting__bare-url">
<label class="setting-label" for="bare-url-input">{t("settings.proxy.bareURL")}</label>
<Input inputName="bare-url" height="50px" />
</div>
<div class="setting__selected-proxy">
<label class="setting-label">{t("settings.proxy.selectedProxy")}</label>
<Dropdown
buttonNameDefault="Ultraviolet"
dropdownList={proxyList}
,
id="dropdown__selected-proxy"
/>
</div>
<div class="setting__search-engine">
<label class="setting-label">{t("settings.proxy.searchEngine")}</label>
<Dropdown
buttonNameDefault="Google"
dropdownList={searchEngineList}
,
id="dropdown__search-engine"
/>
</div>
<div class="setting__open_with">
<label class="setting-label">{t("settings.proxy.openPageWith")}</label>
<Dropdown
buttonNameDefault={t("settings.proxy.openPageWith.embed")}
dropdownList={openPageWith}
,
id="dropdown__open-with"
/>
</div>
<div class="setting__bare-url">
<label class="setting-label" for="bare-url-input">{t("settings.proxy.bareURL")}</label>
<Input inputName="bare-url" height="50px" />
</div>
</div>
<div class="setting__searxng-url">
<label for="searxng-url-input" class="setting-label">{t("settings.proxy.searxngURL")}</label>
<Input height="50px" inputName="searxng-url" defaultTextContent="https://searxng.site/" />
</div>
<label for="searxng-url-input" class="setting-label">{t("settings.proxy.searxngURL")}</label>
<Input height="50px" inputName="searxng-url" defaultTextContent="https://searxng.site/" />
</div>

View file

@ -2,7 +2,7 @@
import ProxyTab from "./SettingsContent/ProxyTab.astro";
import CustomizationTab from "./SettingsContent/CustomizationTab.astro";
import { getLangFromUrl, useTranslations } from "../i18n/utils"
import { getLangFromUrl, useTranslations } from "../i18n/utils";
import CloakingTab from "./SettingsContent/CloakingTab.astro";
import CreditsTab from "./SettingsContent/CreditsTab.astro";
const lang = getLangFromUrl(Astro.url);
@ -10,446 +10,472 @@ const t = useTranslations(lang);
---
<div class="content-hidden">
<div id="content-setting-tab-proxy">
<h1 class="content-setting-header">{t("settings.proxy")}</h1>
<ProxyTab />
</div>
<div id="content-setting-tab-customization">
<h1 class="content-setting-header">{t("settings.customization")}</h1>
<CustomizationTab />
</div>
<div id="content-setting-tab-cloaking">
<h1 class="content-setting-header">{t("settings.cloaking")}</h1>
<p class="content-setting-subtext">{t("settings.cloaking.subtext")}</p>
<CloakingTab />
</div>
<div id="content-setting-tab-credits">
<h1 class="content-setting-header">{t("settings.credits")}</h1>
<CreditsTab />
</div>
<div id="content-setting-tab-proxy">
<h1 class="content-setting-header">{t("settings.proxy")}</h1>
<ProxyTab />
</div>
<div id="content-setting-tab-customization">
<h1 class="content-setting-header">{t("settings.customization")}</h1>
<CustomizationTab />
</div>
<div id="content-setting-tab-cloaking">
<h1 class="content-setting-header">{t("settings.cloaking")}</h1>
<p class="content-setting-subtext">{t("settings.cloaking.subtext")}</p>
<CloakingTab />
</div>
<div id="content-setting-tab-credits">
<h1 class="content-setting-header">{t("settings.credits")}</h1>
<CreditsTab />
</div>
</div>
<div class="popup">
<div class="tabs">
<input type="radio" id="setting-tab-proxy" class="setting-tab" name="tab" checked="true">
<label for="setting-tab-proxy">Proxy</label>
<input type="radio" id="setting-tab-customization" class="setting-tab" name="tab">
<label for="setting-tab-customization">Customization</label>
<input type="radio" id="setting-tab-cloaking" class="setting-tab" name="tab">
<label for="setting-tab-cloaking">Cloaking</label>
<input type="radio" id="setting-tab-credits" class="setting-tab" name="tab">
<label for="setting-tab-credits">Credits</label>
<div class="marker">
<div id="top"></div>
<div id="bottom"></div>
</div>
</div>
<div id="current-content">
<div class="tabs">
<input type="radio" id="setting-tab-proxy" class="setting-tab" name="tab" checked="true" />
<label for="setting-tab-proxy">Proxy</label>
<input type="radio" id="setting-tab-customization" class="setting-tab" name="tab" />
<label for="setting-tab-customization">Customization</label>
<input type="radio" id="setting-tab-cloaking" class="setting-tab" name="tab" />
<label for="setting-tab-cloaking">Cloaking</label>
<input type="radio" id="setting-tab-credits" class="setting-tab" name="tab" />
<label for="setting-tab-credits">Credits</label>
<div class="marker">
<div id="top"></div>
<div id="bottom"></div>
</div>
</div>
<script is:inline>
document.addEventListener("astro:before-swap", () => {
window.currentlySelectedTab = ""
document.removeEventListener('setting-tabChange', determineListener)
})
window.currentlySelectedTab;
window.loadedContentStorage = {}
<div id="current-content"></div>
</div>
<script is:inline>
document.addEventListener("astro:before-swap", () => {
window.currentlySelectedTab = "";
document.removeEventListener("setting-tabChange", determineListener);
});
window.currentlySelectedTab;
window.loadedContentStorage = {};
Array.from(document.getElementsByClassName('setting-tab')).forEach((tab)=>{
let contentToLoad = document.getElementById('content-' + tab.id)
if (contentToLoad) {
window.loadedContentStorage[tab.id] = contentToLoad.innerHTML
contentToLoad.remove()
}
Array.from(document.getElementsByClassName("setting-tab")).forEach((tab) => {
let contentToLoad = document.getElementById("content-" + tab.id);
if (contentToLoad) {
window.loadedContentStorage[tab.id] = contentToLoad.innerHTML;
contentToLoad.remove();
}
tab.addEventListener('click', (event) => {
loadContent(event.target.id)
})
})
tab.addEventListener("click", (event) => {
loadContent(event.target.id);
});
});
function loadContent(tabID) {
if (window.currentlySelectedTab == tabID) return
else window.currentlySelectedTab = tabID
let currentContent = document.getElementById('current-content')
if (currentContent) {
currentContent.style.opacity = '0'
setTimeout(() => {
currentContent.innerHTML = window.loadedContentStorage[tabID]
currentContent.style.opacity = '1'
document.dispatchEvent(new CustomEvent('setting-tabChange', {detail: tabID }))
document.dispatchEvent(new CustomEvent('setting-tabLoad', {detail: tabID }))
}, 250);
function loadContent(tabID) {
if (window.currentlySelectedTab == tabID) return;
else window.currentlySelectedTab = tabID;
let currentContent = document.getElementById("current-content");
if (currentContent) {
currentContent.style.opacity = "0";
setTimeout(() => {
currentContent.innerHTML = window.loadedContentStorage[tabID];
currentContent.style.opacity = "1";
document.dispatchEvent(new CustomEvent("setting-tabChange", { detail: tabID }));
document.dispatchEvent(new CustomEvent("setting-tabLoad", { detail: tabID }));
}, 250);
}
}
function addDropdownListener() {
let dropdown_toggles = document.getElementsByClassName("dropdown-toggle");
Array.from(dropdown_toggles).forEach((toggle) => {
toggle.addEventListener("click", () => {
let dropdown = document.getElementById(toggle.id + "-menu");
if (dropdown) {
if (dropdown.style.maxHeight == "0px" || dropdown.style.maxHeight == "") {
dropdown.style.maxHeight = dropdown.scrollHeight + "px";
toggle.style.borderRadius = "10px 10px 0 0";
} else {
dropdown.style.maxHeight = "0px";
setTimeout(() => {
toggle.style.borderRadius = "10px";
}, 300);
}
}
}
});
});
}
function addDropdownListener() {
let dropdown_toggles = document.getElementsByClassName('dropdown-toggle')
Array.from(dropdown_toggles).forEach((toggle) => {
toggle.addEventListener('click', () => {
let dropdown = document.getElementById(toggle.id + "-menu")
if (dropdown) {
if (dropdown.style.maxHeight == '0px' || dropdown.style.maxHeight == '') {
dropdown.style.maxHeight = dropdown.scrollHeight + 'px';
toggle.style.borderRadius = '10px 10px 0 0';
} else {
dropdown.style.maxHeight = '0px';
setTimeout(() => {
toggle.style.borderRadius = '10px';
}, 300);
}
}
})
})
function determineListener(event) {
if (event.detail == "setting-tab-proxy") {
addDropdownListener();
} else if (event.detail == "setting-tab-customization") {
addDropdownListener();
}
}
function determineListener(event) {
if (event.detail == "setting-tab-proxy") {
addDropdownListener()
} else if (event.detail == "setting-tab-customization") {
addDropdownListener()
function closeDropdown(dropdownID) {
let dropdown = document.getElementById(dropdownID);
if (dropdown) {
dropdown.style.maxHeight = "0px";
setTimeout(() => {
let dropdown_toggle = document.getElementById(dropdownID.replace("-menu", ""));
dropdown_toggle.style.borderRadius = "10px";
}, 300);
}
}
function getLocalStorageValue(localStorageItem, dropdownID) {
// I was kinda dumb for not doing this earlier.
let dropdown = document.getElementById(dropdownID);
let dropdownMenu = document.getElementById(dropdownID + "-menu");
if (dropdown && dropdownMenu) {
// Now we find the child that matches localStorageItem.value.
let dropdownItem = Array.from(dropdownMenu.children).find((item) => {
return JSON.parse(localStorage.getItem(localStorageItem)).value == item.dataset.setting;
});
// Now set the inner text to the name in the dropdownItem.
return dropdownItem.innerText;
}
}
function applySavedLocalStorage(localStorageItem, dropdownID) {
if (localStorage.getItem(localStorageItem)) {
let dropdown_toggle = document.getElementById(dropdownID);
if (dropdown_toggle) {
dropdown_toggle.innerText = getLocalStorageValue(localStorageItem, dropdownID);
}
}
}
function closeDropdown(dropdownID) {
let dropdown = document.getElementById(dropdownID)
if (dropdown) {
dropdown.style.maxHeight = '0px';
setTimeout(() => {
let dropdown_toggle = document.getElementById(dropdownID.replace('-menu', ''));
dropdown_toggle.style.borderRadius = '10px';
}, 300);
}
}
function getLocalStorageValue(localStorageItem, dropdownID) {
// I was kinda dumb for not doing this earlier.
let dropdown = document.getElementById(dropdownID);
let dropdownMenu = document.getElementById(dropdownID + "-menu");
if (dropdown && dropdownMenu) {
// Now we find the child that matches localStorageItem.value.
let dropdownItem = Array.from(dropdownMenu.children).find((item) => {
return JSON.parse(localStorage.getItem(localStorageItem)).value == item.dataset.setting
})
// Now set the inner text to the name in the dropdownItem.
return dropdownItem.innerText
}
}
function applySavedLocalStorage(localStorageItem, dropdownID) {
if (localStorage.getItem(localStorageItem)) {
let dropdown_toggle = document.getElementById(dropdownID);
if (dropdown_toggle) {
dropdown_toggle.innerText = getLocalStorageValue(localStorageItem, dropdownID)
function applyDropdownEventListeners(item, dropdownID, localStorageItem, optionalCallback) {
Array.from(item.children).forEach((item) => {
item.addEventListener("click", () => {
let localStorageItemContent = {
name: item.innerText,
value: item.dataset.setting,
};
localStorage.setItem(localStorageItem, JSON.stringify(localStorageItemContent));
applySavedLocalStorage(localStorageItem, dropdownID);
closeDropdown(item.parentElement.id);
if (typeof optionalCallback == "function") {
optionalCallback();
}
}
}
});
});
// Array.from(item.children).forEach((item) => {
// item.addEventListener('click', () => {
// localStorage.setItem(localStorageItem, item.dataset.setting)
// applySavedLocalStorage(localStorageItem, dropdownID)
// closeDropdown(item.parentElement.id);
function applyDropdownEventListeners(item, dropdownID, localStorageItem, optionalCallback) {
Array.from(item.children).forEach((item) => {
item.addEventListener('click', () => {
let localStorageItemContent = {
"name": item.innerText,
"value": item.dataset.setting,
}
localStorage.setItem(localStorageItem, JSON.stringify(localStorageItemContent))
applySavedLocalStorage(localStorageItem, dropdownID)
closeDropdown(item.parentElement.id);
if (typeof optionalCallback == "function") {
optionalCallback();
}
})
})
// Array.from(item.children).forEach((item) => {
// item.addEventListener('click', () => {
// localStorage.setItem(localStorageItem, item.dataset.setting)
// applySavedLocalStorage(localStorageItem, dropdownID)
// closeDropdown(item.parentElement.id);
// if (typeof optionalCallback == "function") {
// optionalCallback();
// }
// })
// })
}
// if (typeof optionalCallback == "function") {
// optionalCallback();
// }
// })
// })
}
function applyInputListeners(item, localStorageItem) {
item.addEventListener("input", () => {
localStorage.setItem(localStorageItem, item.value);
});
}
function applyInputListeners(item, localStorageItem) {
item.addEventListener('input', () => {
localStorage.setItem(localStorageItem, item.value)
})
}
document.addEventListener("setting-tabChange", determineListener);
document.addEventListener('setting-tabChange', determineListener)
loadContent("setting-tab-proxy");
loadContent('setting-tab-proxy')
function setupCustomizationSettings() {
applySavedLocalStorage("alu__selectedTheme", "dropdown__selected-theme");
applySavedLocalStorage("alu__selectedLanguage", "dropdown__selected-language");
let themeDropdown = document.getElementById("dropdown__selected-theme-menu");
let languageDropdown = document.getElementById("dropdown__selected-language-menu");
applyDropdownEventListeners(
themeDropdown,
"dropdown__selected-theme",
"alu__selectedTheme",
changeTheme
);
applyDropdownEventListeners(
languageDropdown,
"dropdown__selected-language",
"alu__selectedLanguage",
navigateToNewLangaugePage
);
}
function setupCustomizationSettings() {
applySavedLocalStorage('alu__selectedTheme', 'dropdown__selected-theme');
applySavedLocalStorage('alu__selectedLanguage', 'dropdown__selected-language');
let themeDropdown = document.getElementById('dropdown__selected-theme-menu')
let languageDropdown = document.getElementById('dropdown__selected-language-menu')
applyDropdownEventListeners(themeDropdown, 'dropdown__selected-theme', 'alu__selectedTheme', changeTheme);
applyDropdownEventListeners(languageDropdown, 'dropdown__selected-language', 'alu__selectedLanguage', navigateToNewLangaugePage);
}
function setupCloakingSettings() {
Array.from(document.getElementById("cloak-list").children).forEach((cloak) => {
cloak.addEventListener("click", () => {
let cloakName = cloak.dataset.cloakName;
let cloakIcon = cloak.dataset.cloakIcon;
function setupCloakingSettings() {
Array.from(document.getElementById('cloak-list').children).forEach((cloak) => {
cloak.addEventListener('click', () => {
let cloakName = cloak.dataset.cloakName
let cloakIcon = cloak.dataset.cloakIcon
let localStorageItem = {
"name": cloakName,
"icon": cloakIcon,
"isCustom": false,
}
localStorage.setItem('alu__selectedCloak', JSON.stringify(localStorageItem))
if (cloakName == "None") {
localStorage.removeItem('alu__selectedCloak')
cloakName = "Settings | Alu"
cloakIcon = "/favicon.png"
}
let link = document.querySelector("link[rel~='icon']");
if (!link) {
link = document.createElement('link');
link.rel = 'icon';
document.head.appendChild(link);
}
link.href = cloakIcon;
document.title = cloakName;
if (!cloak.classList.contains("selected")) {
Array.from(document.getElementById('cloak-list').children).forEach((cloak2) => {
cloak2.classList.remove('selected')
})
cloak.classList.add('selected')
}
})
})
let customNameInput = document.getElementById('cloak-custom-name-input')
let customFaviconInput = document.getElementById('cloak-custom-favicon-input')
if (localStorage.getItem('alu__selectedCloak')) {
let selectedCloak = JSON.parse(localStorage.getItem('alu__selectedCloak'))
if (selectedCloak.isCustom) {
customNameInput.value = selectedCloak.name
customFaviconInput.value = selectedCloak.icon
}
}
document.getElementById('cloak-custom-button').addEventListener("click", () => {
let cloakName = document.getElementById('cloak-custom-name-input').value
let cloakIcon = document.getElementById('cloak-custom-favicon-input').value
let localStorageItem = {
"name": cloakName,
"icon": cloakIcon,
"isCustom": true,
}
localStorage.setItem('alu__selectedCloak', JSON.stringify(localStorageItem))
name: cloakName,
icon: cloakIcon,
isCustom: false,
};
localStorage.setItem("alu__selectedCloak", JSON.stringify(localStorageItem));
if (cloakName == "None") {
localStorage.removeItem('alu__selectedCloak')
cloakName = "Settings | Alu"
cloakIcon = "/favicon.png"
}
localStorage.removeItem("alu__selectedCloak");
cloakName = "Settings | Alu";
cloakIcon = "/favicon.png";
}
let link = document.querySelector("link[rel~='icon']");
if (!link) {
link = document.createElement('link');
link.rel = 'icon';
document.head.appendChild(link);
link = document.createElement("link");
link.rel = "icon";
document.head.appendChild(link);
}
link.href = cloakIcon;
document.title = cloakName;
})
}
function changeTheme() {
let theme = JSON.parse(localStorage.getItem('alu__selectedTheme')).value
if (theme) {
document.documentElement.setAttribute('data-theme', theme.toLowerCase())
let footer = document.getElementById('footer');
if (footer) {
footer.dataset.theme = theme.toLowerCase();
if (!cloak.classList.contains("selected")) {
Array.from(document.getElementById("cloak-list").children).forEach((cloak2) => {
cloak2.classList.remove("selected");
});
cloak.classList.add("selected");
}
});
});
let customNameInput = document.getElementById("cloak-custom-name-input");
let customFaviconInput = document.getElementById("cloak-custom-favicon-input");
if (localStorage.getItem("alu__selectedCloak")) {
let selectedCloak = JSON.parse(localStorage.getItem("alu__selectedCloak"));
if (selectedCloak.isCustom) {
customNameInput.value = selectedCloak.name;
customFaviconInput.value = selectedCloak.icon;
}
}
function setupSettings(event) {
if (event.detail == "setting-tab-proxy") {
applySavedLocalStorage('alu__selectedProxy', 'dropdown__selected-proxy');
applySavedLocalStorage('alu__search_engine', 'dropdown__search-engine');
applySavedLocalStorage("alu__selectedOpenWith", 'dropdown__open-with');
let selectedProxyDropdown = document.getElementById('dropdown__selected-proxy-menu')
let searchEngineDropdown = document.getElementById('dropdown__search-engine-menu')
let openWithDropdown = document.getElementById('dropdown__open-with-menu')
let bareUrlInput = document.getElementById('bare-url-input')
let searxngUrlInput = document.getElementById('searxng-url-input')
let savedSearxngUrl = localStorage.getItem("alu__searxngUrl")
if (savedSearxngUrl != undefined) {
if (savedSearxngUrl == "") localStorage.setItem("alu__searxngUrl", "https://searxng.site/")
searxngUrlInput.value = localStorage.getItem("alu__searxngUrl")
}
// Proxy settings
applyInputListeners(bareUrlInput, 'alu__bareUrl')
applyInputListeners(searxngUrlInput, 'alu__searxngUrl')
applyDropdownEventListeners(searchEngineDropdown, 'dropdown__search-engine', 'alu__search_engine', checkSearxng);
applyDropdownEventListeners(selectedProxyDropdown, 'dropdown__selected-proxy', 'alu__selectedProxy');
applyDropdownEventListeners(openWithDropdown, 'dropdown__open-with', 'alu__selectedOpenWith');
if (localStorage.getItem('bareUrl')) {
bareUrlInput.value = localStorage.getItem('bareUrl')
} else {
bareUrlInput.value = '/bare/'
}
checkSearxng();
} else if (event.detail == "setting-tab-customization") {
setupCustomizationSettings();
} else if (event.detail == "setting-tab-cloaking") {
setupCloakingSettings();
}
}
document.getElementById("cloak-custom-button").addEventListener("click", () => {
let cloakName = document.getElementById("cloak-custom-name-input").value;
let cloakIcon = document.getElementById("cloak-custom-favicon-input").value;
let localStorageItem = {
name: cloakName,
icon: cloakIcon,
isCustom: true,
};
localStorage.setItem("alu__selectedCloak", JSON.stringify(localStorageItem));
if (cloakName == "None") {
localStorage.removeItem("alu__selectedCloak");
cloakName = "Settings | Alu";
cloakIcon = "/favicon.png";
}
let link = document.querySelector("link[rel~='icon']");
if (!link) {
link = document.createElement("link");
link.rel = "icon";
document.head.appendChild(link);
}
link.href = cloakIcon;
document.title = cloakName;
});
}
function checkSearxng() {
// This function checks if the "searxng" option was clicked, display an additional option if so.
if (localStorage.getItem("alu__search_engine")) {
if (JSON.parse(localStorage.getItem("alu__search_engine")).value.toLowerCase() == "searx") {
document.getElementsByClassName('setting__searxng-url')[0].style.opacity = '1'
} else {
document.getElementsByClassName('setting__searxng-url')[0].style.opacity = '0'
}
function changeTheme() {
let theme = JSON.parse(localStorage.getItem("alu__selectedTheme")).value;
if (theme) {
document.documentElement.setAttribute("data-theme", theme.toLowerCase());
let footer = document.getElementById("footer");
if (footer) {
footer.dataset.theme = theme.toLowerCase();
}
}
}
document.addEventListener('setting-tabLoad', setupSettings)
function setupSettings(event) {
if (event.detail == "setting-tab-proxy") {
applySavedLocalStorage("alu__selectedProxy", "dropdown__selected-proxy");
applySavedLocalStorage("alu__search_engine", "dropdown__search-engine");
applySavedLocalStorage("alu__selectedOpenWith", "dropdown__open-with");
let selectedProxyDropdown = document.getElementById("dropdown__selected-proxy-menu");
let searchEngineDropdown = document.getElementById("dropdown__search-engine-menu");
let openWithDropdown = document.getElementById("dropdown__open-with-menu");
let bareUrlInput = document.getElementById("bare-url-input");
let searxngUrlInput = document.getElementById("searxng-url-input");
let savedSearxngUrl = localStorage.getItem("alu__searxngUrl");
if (savedSearxngUrl != undefined) {
if (savedSearxngUrl == "") localStorage.setItem("alu__searxngUrl", "https://searxng.site/");
searxngUrlInput.value = localStorage.getItem("alu__searxngUrl");
}
// Proxy settings
applyInputListeners(bareUrlInput, "alu__bareUrl");
applyInputListeners(searxngUrlInput, "alu__searxngUrl");
applyDropdownEventListeners(
searchEngineDropdown,
"dropdown__search-engine",
"alu__search_engine",
checkSearxng
);
applyDropdownEventListeners(
selectedProxyDropdown,
"dropdown__selected-proxy",
"alu__selectedProxy"
);
applyDropdownEventListeners(openWithDropdown, "dropdown__open-with", "alu__selectedOpenWith");
if (localStorage.getItem("bareUrl")) {
bareUrlInput.value = localStorage.getItem("bareUrl");
} else {
bareUrlInput.value = "/bare/";
}
checkSearxng();
} else if (event.detail == "setting-tab-customization") {
setupCustomizationSettings();
} else if (event.detail == "setting-tab-cloaking") {
setupCloakingSettings();
}
}
function navigateToNewLangaugePage() {
let value = JSON.parse(localStorage.getItem("alu__selectedLanguage")).value
let currentLanguage = window.location.pathname.split("/")[1]
// Do nothing.. because we're already on the page.
if (value == currentLanguage) return;
switch (value) {
case "en":
window.location.href = "/en/settings/"
break;
case "jp":
window.location.href = "/jp/settings/"
break;
function checkSearxng() {
// This function checks if the "searxng" option was clicked, display an additional option if so.
if (localStorage.getItem("alu__search_engine")) {
if (JSON.parse(localStorage.getItem("alu__search_engine")).value.toLowerCase() == "searx") {
document.getElementsByClassName("setting__searxng-url")[0].style.opacity = "1";
} else {
document.getElementsByClassName("setting__searxng-url")[0].style.opacity = "0";
}
}
}
document.addEventListener("setting-tabLoad", setupSettings);
function navigateToNewLangaugePage() {
let value = JSON.parse(localStorage.getItem("alu__selectedLanguage")).value;
let currentLanguage = window.location.pathname.split("/")[1];
// Do nothing.. because we're already on the page.
if (value == currentLanguage) return;
switch (value) {
case "en":
window.location.href = "/en/settings/";
break;
case "jp":
window.location.href = "/jp/settings/";
break;
}
}
</script>
<style is:global>
.content-hidden {
.content-hidden {
display: none;
}
#current-content {
}
#current-content {
transition: opacity 250ms ease-in-out;
margin-left: 20px;
}
.settings-container {
}
.settings-container {
display: flex;
gap: 20px;
position: relative;
z-index: 2;
}
.content-setting-header {
color: var(--text-color);
font-weight: 400;
}
.content-setting-subtext {
font-size: 20px;
color: var(--text-color-accent);
}
}
.content-setting-header {
color: var(--text-color);
font-weight: 400;
}
.content-setting-subtext {
font-size: 20px;
color: var(--text-color-accent);
}
.setting-label {
color: var(--text-color);
font-size: 18px;
font-weight: 400;
/* annoying stuff with label elements. */
display: block;
opacity: 1.0;
margin-block: 1em;
margin-inline: 0;
user-select: none;
width: 100%;
cursor: auto;
}
.setting-label {
color: var(--text-color);
font-size: 18px;
font-weight: 400;
/* annoying stuff with label elements. */
display: block;
opacity: 1;
margin-block: 1em;
margin-inline: 0;
user-select: none;
width: 100%;
cursor: auto;
}
.setting__searxng-url {
margin-top: 10px;
opacity: 0;
transition: opacity 250ms ease-in-out;
}
label {
font-size: 24px;
font-weight: 700;
cursor: pointer;
color: #d8d8d8;
opacity: .6;
transition: opacity .4s ease-in-out;
display: block;
width: calc(100% - 48px) ;
text-align: right;
z-index: 100;
user-select: none;
text-align: start;
margin-left: 20px;
}
input[type="radio"]{
display: none;
width: 0;
}
label:hover, input[type="radio"]:checked+label {
opacity: 1;
}
.popup{
width: 98%;
height: 80%;
margin: 0 auto;
min-height: 400px;
max-height: 400px;
border-radius: 48px;
box-sizing: border-box;
background-color: var(--background-color);
overflow: hidden;
display: flex;
align-self: center;
}
.tabs{
width: 100%;
max-width: 240px;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-around;
position: relative;
gap: 25px
}
.marker{
position: absolute;
width: 100%;
height: 200%;
display: flex;
flex-direction: column;
top: calc(-100% );
left: 0;
transition: transform .2s ease-in-out;
background-color: var(--accent-color);
}
.marker #bottom, .marker #top{
background-color: var(--background-highlight);
}
.marker #top{
height: calc(50%);
margin-bottom: auto;
border-radius: 0 0 32px 0;
}
.marker #bottom{
height: calc(50% - 72px);
border-radius: 0 32px 0 0;
}
#setting-tab-proxy:checked ~ .marker{transform: translateY(0%)}
#setting-tab-customization:checked ~ .marker{transform: translateY(13.5%)}
#setting-tab-cloaking:checked ~ .marker{transform: translateY(27%)}
#setting-tab-credits:checked ~ .marker{transform: translateY(41%)}
</style>
.setting__searxng-url {
margin-top: 10px;
opacity: 0;
transition: opacity 250ms ease-in-out;
}
label {
font-size: 24px;
font-weight: 700;
cursor: pointer;
color: #d8d8d8;
opacity: 0.6;
transition: opacity 0.4s ease-in-out;
display: block;
width: calc(100% - 48px);
text-align: right;
z-index: 100;
user-select: none;
text-align: start;
margin-left: 20px;
}
input[type="radio"] {
display: none;
width: 0;
}
label:hover,
input[type="radio"]:checked + label {
opacity: 1;
}
.popup {
width: 98%;
height: 80%;
margin: 0 auto;
min-height: 400px;
max-height: 400px;
border-radius: 48px;
box-sizing: border-box;
background-color: var(--background-color);
overflow: hidden;
display: flex;
align-self: center;
}
.tabs {
width: 100%;
max-width: 240px;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-around;
position: relative;
gap: 25px;
}
.marker {
position: absolute;
width: 100%;
height: 200%;
display: flex;
flex-direction: column;
top: calc(-100%);
left: 0;
transition: transform 0.2s ease-in-out;
background-color: var(--accent-color);
}
.marker #bottom,
.marker #top {
background-color: var(--background-highlight);
}
.marker #top {
height: calc(50%);
margin-bottom: auto;
border-radius: 0 0 32px 0;
}
.marker #bottom {
height: calc(50% - 72px);
border-radius: 0 32px 0 0;
}
#setting-tab-proxy:checked ~ .marker {
transform: translateY(0%);
}
#setting-tab-customization:checked ~ .marker {
transform: translateY(13.5%);
}
#setting-tab-cloaking:checked ~ .marker {
transform: translateY(27%);
}
#setting-tab-credits:checked ~ .marker {
transform: translateY(41%);
}
</style>

View file

@ -3,159 +3,190 @@ const { inputID } = Astro.props;
---
<label class="switch">
<input id={inputID} checked="" type="checkbox">
<div class="slider">
<div class="circle">
<svg class="cross" xml:space="preserve" style="enable-background:new 0 0 512 512" viewBox="0 0 365.696 365.696" y="0" x="0" height="6" width="6" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g>
<path data-original="#000000" fill="currentColor" d="M243.188 182.86 356.32 69.726c12.5-12.5 12.5-32.766 0-45.247L341.238 9.398c-12.504-12.503-32.77-12.503-45.25 0L182.86 122.528 69.727 9.374c-12.5-12.5-32.766-12.5-45.247 0L9.375 24.457c-12.5 12.504-12.5 32.77 0 45.25l113.152 113.152L9.398 295.99c-12.503 12.503-12.503 32.769 0 45.25L24.48 356.32c12.5 12.5 32.766 12.5 45.247 0l113.132-113.132L295.99 356.32c12.503 12.5 32.769 12.5 45.25 0l15.081-15.082c12.5-12.504 12.5-32.77 0-45.25zm0 0"></path>
</g>
</svg>
<svg class="checkmark" xml:space="preserve" style="enable-background:new 0 0 512 512" viewBox="0 0 24 24" y="0" x="0" height="10" width="10" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g>
<path class="" data-original="#000000" fill="currentColor" d="M9.707 19.121a.997.997 0 0 1-1.414 0l-5.646-5.647a1.5 1.5 0 0 1 0-2.121l.707-.707a1.5 1.5 0 0 1 2.121 0L9 14.171l9.525-9.525a1.5 1.5 0 0 1 2.121 0l.707.707a1.5 1.5 0 0 1 0 2.121z"></path>
</g>
</svg>
</div>
<input id={inputID} checked="" type="checkbox" />
<div class="slider">
<div class="circle">
<svg
class="cross"
xml:space="preserve"
style="enable-background:new 0 0 512 512"
viewBox="0 0 365.696 365.696"
y="0"
x="0"
height="6"
width="6"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1"
xmlns="http://www.w3.org/2000/svg">
<g>
<path
data-original="#000000"
fill="currentColor"
d="M243.188 182.86 356.32 69.726c12.5-12.5 12.5-32.766 0-45.247L341.238 9.398c-12.504-12.503-32.77-12.503-45.25 0L182.86 122.528 69.727 9.374c-12.5-12.5-32.766-12.5-45.247 0L9.375 24.457c-12.5 12.504-12.5 32.77 0 45.25l113.152 113.152L9.398 295.99c-12.503 12.503-12.503 32.769 0 45.25L24.48 356.32c12.5 12.5 32.766 12.5 45.247 0l113.132-113.132L295.99 356.32c12.503 12.5 32.769 12.5 45.25 0l15.081-15.082c12.5-12.504 12.5-32.77 0-45.25zm0 0"
></path>
</g>
</svg>
<svg
class="checkmark"
xml:space="preserve"
style="enable-background:new 0 0 512 512"
viewBox="0 0 24 24"
y="0"
x="0"
height="10"
width="10"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1"
xmlns="http://www.w3.org/2000/svg">
<g>
<path
class=""
data-original="#000000"
fill="currentColor"
d="M9.707 19.121a.997.997 0 0 1-1.414 0l-5.646-5.647a1.5 1.5 0 0 1 0-2.121l.707-.707a1.5 1.5 0 0 1 2.121 0L9 14.171l9.525-9.525a1.5 1.5 0 0 1 2.121 0l.707.707a1.5 1.5 0 0 1 0 2.121z"
></path>
</g>
</svg>
</div>
</div>
</label>
<style>
.switch {
--switch-width: 46px;
--switch-height: 24px;
--switch-bg: rgb(131, 131, 131);
--switch-checked-bg: rgb(0, 218, 80);
--switch-offset: calc((var(--switch-height) - var(--circle-diameter)) / 2);
--switch-transition: all .2s cubic-bezier(0.27, 0.2, 0.25, 1.51);
--circle-diameter: 18px;
--circle-bg: #fff;
--circle-shadow: 1px 1px 2px rgba(146, 146, 146, 0.45);
--circle-checked-shadow: -1px 1px 2px rgba(163, 163, 163, 0.45);
--circle-transition: var(--switch-transition);
--icon-transition: all .2s cubic-bezier(0.27, 0.2, 0.25, 1.51);
--icon-cross-color: var(--switch-bg);
--icon-cross-size: 6px;
--icon-checkmark-color: var(--switch-checked-bg);
--icon-checkmark-size: 10px;
--effect-width: calc(var(--circle-diameter) / 2);
--effect-height: calc(var(--effect-width) / 2 - 1px);
--effect-bg: var(--circle-bg);
--effect-border-radius: 1px;
--effect-transition: all .2s ease-in-out;
}
.switch {
--switch-width: 46px;
--switch-height: 24px;
--switch-bg: rgb(131, 131, 131);
--switch-checked-bg: rgb(0, 218, 80);
--switch-offset: calc((var(--switch-height) - var(--circle-diameter)) / 2);
--switch-transition: all 0.2s cubic-bezier(0.27, 0.2, 0.25, 1.51);
--circle-diameter: 18px;
--circle-bg: #fff;
--circle-shadow: 1px 1px 2px rgba(146, 146, 146, 0.45);
--circle-checked-shadow: -1px 1px 2px rgba(163, 163, 163, 0.45);
--circle-transition: var(--switch-transition);
--icon-transition: all 0.2s cubic-bezier(0.27, 0.2, 0.25, 1.51);
--icon-cross-color: var(--switch-bg);
--icon-cross-size: 6px;
--icon-checkmark-color: var(--switch-checked-bg);
--icon-checkmark-size: 10px;
--effect-width: calc(var(--circle-diameter) / 2);
--effect-height: calc(var(--effect-width) / 2 - 1px);
--effect-bg: var(--circle-bg);
--effect-border-radius: 1px;
--effect-transition: all 0.2s ease-in-out;
}
.switch input {
display: none;
}
.switch input {
display: none;
}
.switch {
display: inline-block;
}
.switch {
display: inline-block;
}
.switch svg {
-webkit-transition: var(--icon-transition);
-o-transition: var(--icon-transition);
transition: var(--icon-transition);
position: absolute;
height: auto;
}
.switch svg {
-webkit-transition: var(--icon-transition);
-o-transition: var(--icon-transition);
transition: var(--icon-transition);
position: absolute;
height: auto;
}
.switch .checkmark {
width: var(--icon-checkmark-size);
color: var(--icon-checkmark-color);
-webkit-transform: scale(0);
-ms-transform: scale(0);
transform: scale(0);
}
.switch .checkmark {
width: var(--icon-checkmark-size);
color: var(--icon-checkmark-color);
-webkit-transform: scale(0);
-ms-transform: scale(0);
transform: scale(0);
}
.switch .cross {
width: var(--icon-cross-size);
color: var(--icon-cross-color);
}
.switch .cross {
width: var(--icon-cross-size);
color: var(--icon-cross-color);
}
.slider {
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: var(--switch-width);
height: var(--switch-height);
background: var(--switch-bg);
border-radius: 999px;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
position: relative;
-webkit-transition: var(--switch-transition);
-o-transition: var(--switch-transition);
transition: var(--switch-transition);
cursor: pointer;
}
.slider {
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: var(--switch-width);
height: var(--switch-height);
background: var(--switch-bg);
border-radius: 999px;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
position: relative;
-webkit-transition: var(--switch-transition);
-o-transition: var(--switch-transition);
transition: var(--switch-transition);
cursor: pointer;
}
.circle {
width: var(--circle-diameter);
height: var(--circle-diameter);
background: var(--circle-bg);
border-radius: inherit;
-webkit-box-shadow: var(--circle-shadow);
box-shadow: var(--circle-shadow);
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-transition: var(--circle-transition);
-o-transition: var(--circle-transition);
transition: var(--circle-transition);
z-index: 1;
position: absolute;
left: var(--switch-offset);
}
.circle {
width: var(--circle-diameter);
height: var(--circle-diameter);
background: var(--circle-bg);
border-radius: inherit;
-webkit-box-shadow: var(--circle-shadow);
box-shadow: var(--circle-shadow);
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-transition: var(--circle-transition);
-o-transition: var(--circle-transition);
transition: var(--circle-transition);
z-index: 1;
position: absolute;
left: var(--switch-offset);
}
.slider::before {
content: "";
position: absolute;
width: var(--effect-width);
height: var(--effect-height);
left: calc(var(--switch-offset) + (var(--effect-width) / 2));
background: var(--effect-bg);
border-radius: var(--effect-border-radius);
-webkit-transition: var(--effect-transition);
-o-transition: var(--effect-transition);
transition: var(--effect-transition);
}
.slider::before {
content: "";
position: absolute;
width: var(--effect-width);
height: var(--effect-height);
left: calc(var(--switch-offset) + (var(--effect-width) / 2));
background: var(--effect-bg);
border-radius: var(--effect-border-radius);
-webkit-transition: var(--effect-transition);
-o-transition: var(--effect-transition);
transition: var(--effect-transition);
}
/* actions */
/* actions */
.switch input:checked+.slider {
background: var(--switch-checked-bg);
}
.switch input:checked + .slider {
background: var(--switch-checked-bg);
}
.switch input:checked+.slider .checkmark {
-webkit-transform: scale(1);
-ms-transform: scale(1);
transform: scale(1);
}
.switch input:checked + .slider .checkmark {
-webkit-transform: scale(1);
-ms-transform: scale(1);
transform: scale(1);
}
.switch input:checked+.slider .cross {
-webkit-transform: scale(0);
-ms-transform: scale(0);
transform: scale(0);
}
.switch input:checked + .slider .cross {
-webkit-transform: scale(0);
-ms-transform: scale(0);
transform: scale(0);
}
.switch input:checked+.slider::before {
left: calc(100% - var(--effect-width) - (var(--effect-width) / 2) - var(--switch-offset));
}
.switch input:checked + .slider::before {
left: calc(100% - var(--effect-width) - (var(--effect-width) / 2) - var(--switch-offset));
}
.switch input:checked+.slider .circle {
left: calc(100% - var(--circle-diameter) - var(--switch-offset));
-webkit-box-shadow: var(--circle-checked-shadow);
box-shadow: var(--circle-checked-shadow);
}
</style>
.switch input:checked + .slider .circle {
left: calc(100% - var(--circle-diameter) - var(--switch-offset));
-webkit-box-shadow: var(--circle-checked-shadow);
box-shadow: var(--circle-checked-shadow);
}
</style>

View file

@ -1,18 +1,19 @@
<script>
function switchTheme() {
let currentTheme = localStorage.getItem("alu__selectedTheme")
function switchTheme() {
let currentTheme = localStorage.getItem("alu__selectedTheme");
if (currentTheme) {
document.documentElement.setAttribute("data-theme", JSON.parse(currentTheme).value.toLowerCase());
let footer = document.getElementById('footer');
if (footer) {
footer.dataset.theme = JSON.parse(currentTheme).value.toLowerCase();
}
}
if (currentTheme) {
document.documentElement.setAttribute(
"data-theme",
JSON.parse(currentTheme).value.toLowerCase()
);
let footer = document.getElementById("footer");
if (footer) {
footer.dataset.theme = JSON.parse(currentTheme).value.toLowerCase();
}
}
switchTheme()
}
switchTheme();
document.addEventListener('astro:after-swap', switchTheme)
</script>
document.addEventListener("astro:after-swap", switchTheme);
</script>

View file

@ -1,82 +0,0 @@
<script src="/uv/uv.bundle.js" transition:persist is:inline></script>
<script src="/uv.config.js" transition:persist is:inline></script>
<script transition:persist defer>
// This is a hack to make sure window.__uv$config is defined, because this means everything has been.
if (window.__uv$config == undefined) window.location.reload();
var form = document.querySelector('form');
var input = document.querySelector('input');
window.navigator.serviceWorker.register('/sw.js', {
scope: window.__uv$config.prefix
})
form.addEventListener('submit', (event) => {
event.preventDefault();
let loadingContent = document.getElementById('loading-content');
loadingContent.style.opacity = 1;
let url = input.value.trim();
if (!isUrl(url)) url = getSearchEngine() + url;
else if (!(url.startsWith('https://') || url.startsWith('http://'))) url = 'http://' + url;
let iframe = document.getElementById('proxy-frame');
iframe.src = window.__uv$config.prefix + window.__uv$config.encodeUrl(url);
iframe.style.pointerEvents = "auto";
iframe.classList.add("proxy-frame");
document.body.appendChild(iframe);
iframe.addEventListener('load', () => {
let topBar = document.getElementById('top-bar');
loadingContent.style.opacity = 0;
topBar.innerHTML = "";
topBar.classList.add("top-bar");
let closeButton = document.createElement('button');
closeButton.classList.add("close-button")
closeButton.innerText = "Close";
closeButton.addEventListener('click', () => {
iframe.style.opacity = 0;
topBar.style.opacity = 0;
iframe.style.pointerEvents = "none";
topBar.style.pointerEvents = "none";
});
let urlText = document.createElement('p');
urlText.classList.add("url-text");
urlText.innerText = window.__uv$config.decodeUrl(iframe.src.split(__uv$config.prefix)[1]);
iframe.style.opacity = 1;
topBar.style.opacity = 1;
topBar.style.pointerEvents = "auto";
topBar.appendChild(closeButton);
topBar.appendChild(urlText);
document.body.appendChild(topBar);
})
});
function isUrl(val = ''){
if (/^http(s?):\/\//.test(val) || val.includes('.') && val.substr(0, 1) !== ' ') return true;
return false;
};
function getSearchEngine() {
switch (JSON.parse(localStorage.getItem("alu__search_engine")).value.toLowerCase()) {
case "google": {
return "https://google.com/search?q=";
}
case "bing": {
return "https://bing.com/search?q=d";
}
case "brave": {
return "https://search.brave.com/search?q=";
}
case "searx": {
let localStorageURL = localStorage.getItem("alu__searxngUrl");
if (localStorageURL) {
if (localStorageURL == "") return "https://searxng.site/search?q=";
// Ugly hack to remove the trailing slash :)
if (localStorageURL.endsWith("/")) localStorageURL = localStorageURL.slice(0, -1);
return localStorageURL + "/search?q=";
}
else return "https://searxng.site/search?q=";
}
default: {
return "https://google.com/search?q="
}
}
}
</script>

View file

@ -0,0 +1,105 @@
<svg
width="100%"
height="100%"
preserveAspectRatio="none"
id="svg"
viewBox="0 0 1440 390"
xmlns="http://www.w3.org/2000/svg"
class="transition duration-300 ease-in-out delay-150">
<style>
.path-0 {
animation: pathAnim-0 15s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
@keyframes pathAnim-0 {
0% {
d: path(
"M 0,400 L 0,100 C 124.53333333333336,122.66666666666667 249.06666666666672,145.33333333333334 415,143 C 580.9333333333333,140.66666666666666 788.2666666666667,113.33333333333333 966,102 C 1143.7333333333333,90.66666666666667 1291.8666666666668,95.33333333333334 1440,100 L 1440,400 L 0,400 Z"
);
}
25% {
d: path(
"M 0,400 L 0,100 C 171.19999999999993,71.6 342.39999999999986,43.2 497,54 C 651.6000000000001,64.8 789.6000000000001,114.80000000000001 944,129 C 1098.3999999999999,143.2 1269.1999999999998,121.6 1440,100 L 1440,400 L 0,400 Z"
);
}
50% {
d: path(
"M 0,400 L 0,100 C 186.66666666666669,82.13333333333333 373.33333333333337,64.26666666666667 540,54 C 706.6666666666666,43.733333333333334 853.3333333333333,41.06666666666666 1000,50 C 1146.6666666666667,58.93333333333334 1293.3333333333335,79.46666666666667 1440,100 L 1440,400 L 0,400 Z"
);
}
75% {
d: path(
"M 0,400 L 0,100 C 110.13333333333333,74.53333333333333 220.26666666666665,49.06666666666666 393,58 C 565.7333333333333,66.93333333333334 801.0666666666666,110.26666666666668 986,123 C 1170.9333333333334,135.73333333333332 1305.4666666666667,117.86666666666666 1440,100 L 1440,400 L 0,400 Z"
);
}
100% {
d: path(
"M 0,400 L 0,100 C 124.53333333333336,122.66666666666667 249.06666666666672,145.33333333333334 415,143 C 580.9333333333333,140.66666666666666 788.2666666666667,113.33333333333333 966,102 C 1143.7333333333333,90.66666666666667 1291.8666666666668,95.33333333333334 1440,100 L 1440,400 L 0,400 Z"
);
}
}
</style>
<style media="screen"></style>
<defs>
<linearGradient id="gradient" x1="0%" y1="53%" x2="100%" y2="47%">
<stop offset="5%" stop-color="var(--accent-color)"></stop>
<stop offset="95%" stop-color="var(--accent-color-brighter)"></stop>
</linearGradient>
</defs>
<path
d="M 0,400 L 0,100 C 124.53333333333336,122.66666666666667 249.06666666666672,145.33333333333334 415,143 C 580.9333333333333,140.66666666666666 788.2666666666667,113.33333333333333 966,102 C 1143.7333333333333,90.66666666666667 1291.8666666666668,95.33333333333334 1440,100 L 1440,400 L 0,400 Z"
stroke="none"
stroke-width="0"
fill="url(#gradient)"
fill-opacity="0.53"
class="transition-all duration-300 ease-in-out delay-150 path-0"></path>
<style>
.path-1 {
animation: pathAnim-1 15s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
@keyframes pathAnim-1 {
0% {
d: path(
"M 0,400 L 0,233 C 170.53333333333336,233.66666666666666 341.0666666666667,234.33333333333331 507,236 C 672.9333333333333,237.66666666666669 834.2666666666667,240.33333333333334 989,240 C 1143.7333333333333,239.66666666666666 1291.8666666666668,236.33333333333331 1440,233 L 1440,400 L 0,400 Z"
);
}
25% {
d: path(
"M 0,400 L 0,233 C 202,220.46666666666667 404,207.93333333333334 539,220 C 674,232.06666666666666 742.0000000000001,268.7333333333333 881,275 C 1019.9999999999999,281.2666666666667 1230,257.1333333333333 1440,233 L 1440,400 L 0,400 Z"
);
}
50% {
d: path(
"M 0,400 L 0,233 C 181.33333333333337,210.86666666666667 362.66666666666674,188.73333333333332 512,188 C 661.3333333333333,187.26666666666668 778.6666666666665,207.93333333333334 928,219 C 1077.3333333333335,230.06666666666666 1258.6666666666667,231.53333333333333 1440,233 L 1440,400 L 0,400 Z"
);
}
75% {
d: path(
"M 0,400 L 0,233 C 195.06666666666666,222.86666666666667 390.1333333333333,212.73333333333332 559,216 C 727.8666666666667,219.26666666666668 870.5333333333333,235.93333333333334 1013,241 C 1155.4666666666667,246.06666666666666 1297.7333333333333,239.53333333333333 1440,233 L 1440,400 L 0,400 Z"
);
}
100% {
d: path(
"M 0,400 L 0,233 C 170.53333333333336,233.66666666666666 341.0666666666667,234.33333333333331 507,236 C 672.9333333333333,237.66666666666669 834.2666666666667,240.33333333333334 989,240 C 1143.7333333333333,239.66666666666666 1291.8666666666668,236.33333333333331 1440,233 L 1440,400 L 0,400 Z"
);
}
}
</style>
<style media="screen"></style>
<defs>
<linearGradient id="gradient" x1="0%" y1="53%" x2="100%" y2="47%">
<stop offset="5%" stop-color="#702dc2"></stop>
<stop offset="95%" stop-color="#3d097d"></stop>
</linearGradient>
</defs>
<path
d="M 0,400 L 0,233 C 170.53333333333336,233.66666666666666 341.0666666666667,234.33333333333331 507,236 C 672.9333333333333,237.66666666666669 834.2666666666667,240.33333333333334 989,240 C 1143.7333333333333,239.66666666666666 1291.8666666666668,236.33333333333331 1440,233 L 1440,400 L 0,400 Z"
stroke="none"
stroke-width="0"
fill="url(#gradient)"
fill-opacity="1"
class="transition-all duration-300 ease-in-out delay-150 path-1"></path>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

View file

@ -1,11 +1,40 @@
<script is:inline>
console.log("%cWelcome to Alu", "color: #0251d9; font-size: 2rem; font-weight: bold; text-shadow: 2px 2px 0 #033c9e;");
<script>
declare global {
interface Navigator {
deviceMemory: number;
}
}
console.log("%cSystem Information: ", "color: #025bf5; font-size: 1rem; font-weight: bold;");
console.log("%cOS: %c" + navigator.platform, "color: #025bf5; font-size: 0.75rem; font-weight: bold;", "color: #025bf5; font-size: 0.75rem;");
console.log("%cBrowser: %c" + navigator.userAgent, "color: #025bf5; font-size: 0.75rem; font-weight: bold;", "color: #025bf5; font-size: 0.75rem;");
console.log("%cCPU Cores: %c" + navigator.hardwareConcurrency, "color: #025bf5; font-size: 0.75rem; font-weight: bold;", "color: #025bf5; font-size: 0.75rem;");
// Cmon firefox, do we really not support this?? Basic stuff here from the "indie browser".
console.log("%cMemory: %c" + navigator.deviceMemory + "GB", "color: #025bf5; font-size: 0.75rem; font-weight: bold;", "color: #025bf5; font-size: 0.75rem;");
console.log("%cPlease include this information in a bug report!", "color: #025bf5; font-size: 0.75rem; font-weight: bold;");
</script>
let primaryColor = "#8c25fa";
let secondaryColor = "#601aab";
console.log(
"%cWelcome to Alu",
`color: ${primaryColor}; font-size: 2rem; font-weight: bold; text-shadow: 2px 2px 0 ${secondaryColor};`
);
console.log(
"%cSystem Information: ",
`color: ${primaryColor}; font-size: 1rem; font-weight: bold;`
);
console.log(
"%cOS: " + navigator.platform,
`color: ${primaryColor}; font-size: 0.75rem; font-weight: bold;`
);
console.log(
"%cBrowser: " + navigator.userAgent,
`color: ${primaryColor}; font-size: 0.75rem; font-weight: bold;`
);
console.log(
"%cCPU Cores: " + navigator.hardwareConcurrency,
`color: ${primaryColor}; font-size: 0.75rem; font-weight: bold;`
);
// Cmon firefox, do we really not support this?? Basic stuff here from the "indie browser".
console.log(
"%cMemory: " + navigator.deviceMemory + "GB",
`color: ${primaryColor}; font-size: 0.75rem; font-weight: bold;`
);
console.log(
"%cPlease include this information in a bug report!",
`color: ${primaryColor}; font-size: 0.75rem; font-weight: bold;`
);
</script>

434
src/env.d.ts vendored
View file

@ -1,205 +1,241 @@
/// <reference types="astro/client" />
// ###> astro-i18n/type-generation ###
type PrimaryLocale = "en"
type SecondaryLocale = ""
type Locale = PrimaryLocale | SecondaryLocale
type RouteParameters = {"/games":undefined;"/":undefined;"/settings":undefined;"/404":undefined;}
type Route = keyof RouteParameters
type TranslationVariables = {"nav.brand":object|undefined;"nav.games":object|undefined;"nav.settings":object|undefined;}
type Translation = keyof TranslationVariables
type Environment = "none"|"node"|"browser"
type PrimaryLocale = "en";
type SecondaryLocale = "";
type Locale = PrimaryLocale | SecondaryLocale;
type RouteParameters = {
"/games": undefined;
"/": undefined;
"/settings": undefined;
"/404": undefined;
};
type Route = keyof RouteParameters;
type TranslationVariables = {
"nav.brand": object | undefined;
"nav.games": object | undefined;
"nav.settings": object | undefined;
};
type Translation = keyof TranslationVariables;
type Environment = "none" | "node" | "browser";
declare module "astro-i18n" {
type GetStaticPathsProps = {paginate:Function;rss:Function}
type GetStaticPathsItem = {params:Record<string,number|string|undefined>;props?:Record<string,unknown>}
type DeepStringRecord = {[key: string]:string|DeepStringRecord}
type TranslationDirectory = {i18n?:string;pages?: string}
export type Translations = {[group: string]:{[locale: string]: DeepStringRecord}}
export type TranslationFormatters = {[formatterName: string]:(value:unknown,...args:unknown[])=>unknown}
export type TranslationLoadingRules = {groups:string[];routes: string[]}[]
export type SegmentTranslations = {[secondaryLocale: string]:{[segment: string]:string}}
export interface AstroI18nConfig {primaryLocale:string;secondaryLocales:string[];fallbackLocale:string;showPrimaryLocale:boolean;trailingSlash:"always"|"never";run:"server"|"client+server";translations:Translations;translationLoadingRules:TranslationLoadingRules;translationDirectory:TranslationDirectory;routes:SegmentTranslations;srcDir:string;}
/** Typed astro-i18n config definition. */
export function defineAstroI18nConfig(config: Partial<AstroI18nConfig>): Partial<AstroI18nConfig>
/** The `astro-i18n` middleware. */
export function useAstroI18n(
config?: Partial<AstroI18nConfig> | string,
formatters?: TranslationFormatters,
): (...args: any[]) => any
/** Workaround function to make astroI18n work inside getStaticPaths. This is because Astro's getStaticPaths runs before everything which doesn't allows astroI18n to update its state automatically. */
function createGetStaticPaths(
callback: (
props: GetStaticPathsProps,
) => GetStaticPathsItem[] | Promise<GetStaticPathsItem[]>,
): (props: GetStaticPathsProps & {
astroI18n?: {
locale: string;
};
}) => Promise<GetStaticPathsItem[]>
/**
* @param key The translation key, for example `"my.nested.translation.key"`.
* @param properties An object containing your interpolation variables and/or your variants, for example `{ variant: 3, interpolation: "text" }`.
* @param options `route`: Overrides the current route, you will be able to access that route's translations. `locale`: Overrides the current locale, this allows you to control which language you want to translate to. `fallbackLocale`: Overrides the fallback locale.
*/
export function t<T extends Translation>(
key: T | string & {},
...args: undefined extends TranslationVariables[T]
? [
properties?: keyof TranslationVariables extends T
? Record<string, unknown>
: TranslationVariables[T],
options?: {
route?: Route | string & {}
locale?: Locale | string & {}
fallbackLocale?: Locale | string & {}
}
]
: [
properties: TranslationVariables[T],
options?: {
route?: Route | string & {}
locale?: Locale | string & {}
fallbackLocale?: Locale | string & {}
}
]
): string
/**
* @param route A route in any of the configured languages, for example `"/en/my/english/route/[param]"`.
* @param parameters An object containing your route parameters, for example `{ slug: "my-blog-post-slug" }`.
* @param options `targetLocale`: Overrides the target locale. `routeLocale`: Overrides the given route locale, this is useful if astro-i18n cannot figure out the route's locale. `showPrimaryLocale`: Overrides the showPrimaryLocale parameter. `query`: Adds these query parameters at the end of the translated route.
*/
export function l<T extends Route>(
route: T | string & {},
...args: T extends keyof RouteParameters
? undefined extends RouteParameters[T]
? [
parameters?: Record<string, string>,
options?: {
targetLocale?: string,
routeLocale?: string,
showPrimaryLocale?: string,
query?: Record<string, unknown>
}
]
: [
parameters: RouteParameters[T],
options?: {
targetLocale?: string,
routeLocale?: string,
showPrimaryLocale?: string,
query?: Record<string, unknown>
}
]
: [
parameters?: Record<string, string>,
options?: {
targetLocale?: string,
routeLocale?: string,
showPrimaryLocale?: string,
query?: Record<string, unknown>
}
]
): string
class AstroI18n {
/** The detected runtime environment. */
environment: Environment
/** The current page route. */
route: string
/** All page routes. For example: `["/", "/about", "/posts/[slug]"]` */
pages: string[]
/** The equivalent page for the current route. For example if route is equal to `"/posts/my-cool-cat"` this could return `"/posts/[slug]"`. */
page: string
/** The current page locale. */
locale: Locale
/** All configured locales. */
locales: Locale[]
/** The default/primary locale. */
primaryLocale: PrimaryLocale
/** Locales other than the default/primary one. */
secondaryLocales: SecondaryLocale[]
/** The fallback locale, when a translation is missing in a locale the fallback locale will be used to find a replacement. */
fallbackLocale: Locale
/** True when astro-i18n is initialized. */
isInitialized: boolean
/**
* @param key The translation key, for example `"my.nested.translation.key"`.
* @param properties An object containing your interpolation variables and/or your variants, for example `{ variant: 3, interpolation: "text" }`.
* @param options `route`: Overrides the current route, you will be able to access that route's translations. `locale`: Overrides the current locale, this allows you to control which language you want to translate to. `fallbackLocale`: Overrides the fallback locale.
*/
t<T extends Translation>(
key: T | string & {},
...args: undefined extends TranslationVariables[T]
? [
properties?: keyof TranslationVariables extends T
? Record<string, unknown>
: TranslationVariables[T],
options?: {
route?: Route | string & {}
locale?: Locale | string & {}
fallbackLocale?: Locale | string & {}
}
]
: [
properties: TranslationVariables[T],
options?: {
route?: Route | string & {}
locale?: Locale | string & {}
fallbackLocale?: Locale | string & {}
}
]
): string
/**
* @param route A route in any of the configured languages, for example `"/en/my/english/route/[param]"`.
* @param parameters An object containing your route parameters, for example `{ slug: "my-blog-post-slug" }`.
* @param options `targetLocale`: Overrides the target locale. `routeLocale`: Overrides the given route locale, this is useful if astro-i18n cannot figure out the route's locale. `showPrimaryLocale`: Overrides the showPrimaryLocale parameter. `query`: Adds these query parameters at the end of the translated route.
*/
l<T extends Route>(
route: T | string & {},
...args: T extends keyof RouteParameters
? undefined extends RouteParameters[T]
? [
parameters?: Record<string, string>,
options?: {
targetLocale?: string,
routeLocale?: string,
showPrimaryLocale?: string,
query?: Record<string, unknown>
}
]
: [
parameters: RouteParameters[T],
options?: {
targetLocale?: string,
routeLocale?: string,
showPrimaryLocale?: string,
query?: Record<string, unknown>
}
]
: [
parameters?: Record<string, string>,
options?: {
targetLocale?: string,
routeLocale?: string,
showPrimaryLocale?: string,
query?: Record<string, unknown>
}
]
): string
/** Adds new translations at runtime. */
addTranslations(translations: Translations): this
/** Adds new translation formatters at runtime. */
addFormatters(translationFormatters: TranslationFormatters): this
/** Adds new translation loading rules at runtime. */
addTranslationLoadingRules(translationLoadingRules: TranslationLoadingRules): this
/** Adds new route segment translations at runtime. */
addRoutes(routes: SegmentTranslations): this
/** Tries to parse one of the configured locales out of the given route. If no configured locale is found it will return `null`. */
extractRouteLocale(route: string): string|null
/** Initializes astro-i18n on the server-side. */
initialize(config?: Partial<AstroI18nConfig> | string, formatters?: TranslationFormatters = {}): Promise<void>
/** Redirects the user to the given destination. */
redirect(destination: string | URL, status = 301)
}
export const astroI18n: AstroI18n
type GetStaticPathsProps = { paginate: Function; rss: Function };
type GetStaticPathsItem = {
params: Record<string, number | string | undefined>;
props?: Record<string, unknown>;
};
type DeepStringRecord = { [key: string]: string | DeepStringRecord };
type TranslationDirectory = { i18n?: string; pages?: string };
export type Translations = {
[group: string]: { [locale: string]: DeepStringRecord };
};
export type TranslationFormatters = {
[formatterName: string]: (value: unknown, ...args: unknown[]) => unknown;
};
export type TranslationLoadingRules = {
groups: string[];
routes: string[];
}[];
export type SegmentTranslations = {
[secondaryLocale: string]: { [segment: string]: string };
};
export interface AstroI18nConfig {
primaryLocale: string;
secondaryLocales: string[];
fallbackLocale: string;
showPrimaryLocale: boolean;
trailingSlash: "always" | "never";
run: "server" | "client+server";
translations: Translations;
translationLoadingRules: TranslationLoadingRules;
translationDirectory: TranslationDirectory;
routes: SegmentTranslations;
srcDir: string;
}
/** Typed astro-i18n config definition. */
export function defineAstroI18nConfig(config: Partial<AstroI18nConfig>): Partial<AstroI18nConfig>;
/** The `astro-i18n` middleware. */
export function useAstroI18n(
config?: Partial<AstroI18nConfig> | string,
formatters?: TranslationFormatters
): (...args: any[]) => any;
/** Workaround function to make astroI18n work inside getStaticPaths. This is because Astro's getStaticPaths runs before everything which doesn't allows astroI18n to update its state automatically. */
function createGetStaticPaths(
callback: (props: GetStaticPathsProps) => GetStaticPathsItem[] | Promise<GetStaticPathsItem[]>
): (
props: GetStaticPathsProps & {
astroI18n?: {
locale: string;
};
}
) => Promise<GetStaticPathsItem[]>;
/**
* @param key The translation key, for example `"my.nested.translation.key"`.
* @param properties An object containing your interpolation variables and/or your variants, for example `{ variant: 3, interpolation: "text" }`.
* @param options `route`: Overrides the current route, you will be able to access that route's translations. `locale`: Overrides the current locale, this allows you to control which language you want to translate to. `fallbackLocale`: Overrides the fallback locale.
*/
export function t<T extends Translation>(
key: T | (string & {}),
...args: undefined extends TranslationVariables[T]
? [
properties?: keyof TranslationVariables extends T
? Record<string, unknown>
: TranslationVariables[T],
options?: {
route?: Route | (string & {});
locale?: Locale | (string & {});
fallbackLocale?: Locale | (string & {});
},
]
: [
properties: TranslationVariables[T],
options?: {
route?: Route | (string & {});
locale?: Locale | (string & {});
fallbackLocale?: Locale | (string & {});
},
]
): string;
/**
* @param route A route in any of the configured languages, for example `"/en/my/english/route/[param]"`.
* @param parameters An object containing your route parameters, for example `{ slug: "my-blog-post-slug" }`.
* @param options `targetLocale`: Overrides the target locale. `routeLocale`: Overrides the given route locale, this is useful if astro-i18n cannot figure out the route's locale. `showPrimaryLocale`: Overrides the showPrimaryLocale parameter. `query`: Adds these query parameters at the end of the translated route.
*/
export function l<T extends Route>(
route: T | (string & {}),
...args: T extends keyof RouteParameters
? undefined extends RouteParameters[T]
? [
parameters?: Record<string, string>,
options?: {
targetLocale?: string;
routeLocale?: string;
showPrimaryLocale?: string;
query?: Record<string, unknown>;
},
]
: [
parameters: RouteParameters[T],
options?: {
targetLocale?: string;
routeLocale?: string;
showPrimaryLocale?: string;
query?: Record<string, unknown>;
},
]
: [
parameters?: Record<string, string>,
options?: {
targetLocale?: string;
routeLocale?: string;
showPrimaryLocale?: string;
query?: Record<string, unknown>;
},
]
): string;
class AstroI18n {
/** The detected runtime environment. */
environment: Environment;
/** The current page route. */
route: string;
/** All page routes. For example: `["/", "/about", "/posts/[slug]"]` */
pages: string[];
/** The equivalent page for the current route. For example if route is equal to `"/posts/my-cool-cat"` this could return `"/posts/[slug]"`. */
page: string;
/** The current page locale. */
locale: Locale;
/** All configured locales. */
locales: Locale[];
/** The default/primary locale. */
primaryLocale: PrimaryLocale;
/** Locales other than the default/primary one. */
secondaryLocales: SecondaryLocale[];
/** The fallback locale, when a translation is missing in a locale the fallback locale will be used to find a replacement. */
fallbackLocale: Locale;
/** True when astro-i18n is initialized. */
isInitialized: boolean;
/**
* @param key The translation key, for example `"my.nested.translation.key"`.
* @param properties An object containing your interpolation variables and/or your variants, for example `{ variant: 3, interpolation: "text" }`.
* @param options `route`: Overrides the current route, you will be able to access that route's translations. `locale`: Overrides the current locale, this allows you to control which language you want to translate to. `fallbackLocale`: Overrides the fallback locale.
*/
t<T extends Translation>(
key: T | (string & {}),
...args: undefined extends TranslationVariables[T]
? [
properties?: keyof TranslationVariables extends T
? Record<string, unknown>
: TranslationVariables[T],
options?: {
route?: Route | (string & {});
locale?: Locale | (string & {});
fallbackLocale?: Locale | (string & {});
},
]
: [
properties: TranslationVariables[T],
options?: {
route?: Route | (string & {});
locale?: Locale | (string & {});
fallbackLocale?: Locale | (string & {});
},
]
): string;
/**
* @param route A route in any of the configured languages, for example `"/en/my/english/route/[param]"`.
* @param parameters An object containing your route parameters, for example `{ slug: "my-blog-post-slug" }`.
* @param options `targetLocale`: Overrides the target locale. `routeLocale`: Overrides the given route locale, this is useful if astro-i18n cannot figure out the route's locale. `showPrimaryLocale`: Overrides the showPrimaryLocale parameter. `query`: Adds these query parameters at the end of the translated route.
*/
l<T extends Route>(
route: T | (string & {}),
...args: T extends keyof RouteParameters
? undefined extends RouteParameters[T]
? [
parameters?: Record<string, string>,
options?: {
targetLocale?: string;
routeLocale?: string;
showPrimaryLocale?: string;
query?: Record<string, unknown>;
},
]
: [
parameters: RouteParameters[T],
options?: {
targetLocale?: string;
routeLocale?: string;
showPrimaryLocale?: string;
query?: Record<string, unknown>;
},
]
: [
parameters?: Record<string, string>,
options?: {
targetLocale?: string;
routeLocale?: string;
showPrimaryLocale?: string;
query?: Record<string, unknown>;
},
]
): string;
/** Adds new translations at runtime. */
addTranslations(translations: Translations): this;
/** Adds new translation formatters at runtime. */
addFormatters(translationFormatters: TranslationFormatters): this;
/** Adds new translation loading rules at runtime. */
addTranslationLoadingRules(translationLoadingRules: TranslationLoadingRules): this;
/** Adds new route segment translations at runtime. */
addRoutes(routes: SegmentTranslations): this;
/** Tries to parse one of the configured locales out of the given route. If no configured locale is found it will return `null`. */
extractRouteLocale(route: string): string | null;
/** Initializes astro-i18n on the server-side. */
initialize(
config?: Partial<AstroI18nConfig> | string,
formatters?: TranslationFormatters = {}
): Promise<void>;
/** Redirects the user to the given destination. */
redirect(destination: string | URL, status = 301);
}
export const astroI18n: AstroI18n;
}
// ###< astro-i18n/type-generation ###

View file

@ -1,48 +1,48 @@
{
"ultraviolet": "Ultraviolet",
"ultraviolet": "Ultraviolet",
"pages.home": "Home | Alu",
"pages.games": "Games | Alu",
"pages.settings": "Settings | Alu",
"pages.home": "Home | Alu",
"pages.games": "Games | Alu",
"pages.settings": "Settings | Alu",
"nav.brand": "Alu",
"nav.games": "Games",
"nav.settings": "Settings",
"nav.brand": "Alu",
"nav.games": "Games",
"nav.settings": "Settings",
"menu.welcome": "Welcome to Alu",
"menu.search": "Search...",
"menu.welcome": "Welcome to Alu",
"menu.search": "Search...",
"footer.brand": "Alu",
"footer.madeWithLove": "Made with ❤️ by wearr",
"footer.poweredBy": "Powered by Ultraviolet",
"footer.services": "Services",
"footer.socials": "Socials",
"footer.aluProject": "Alu Project",
"footer.brand": "Alu",
"footer.madeWithLove": "Made with ❤️ by wearr",
"footer.services": "Services",
"footer.socials": "Socials",
"footer.aluProject": "Alu Project",
"games.title": "Games",
"games.title": "Games",
"settings.title": "Settings",
"settings.proxy": "Proxy",
"settings.proxy.auto": "Auto",
"settings.proxy.selectedProxy": "Selected Proxy",
"settings.proxy.searchEngine": "Search Engine",
"settings.proxy.openPageWith": "Open With",
"settings.proxy.openPageWith.embed": "Embed",
"settings.proxy.openPageWith.newTab": "New Tab",
"settings.proxy.searxngURL": "Searx URL",
"settings.proxy.bareURL": "Bare URL",
"settings.title": "Settings",
"settings.proxy": "Proxy",
"settings.proxy.auto": "Auto",
"settings.proxy.selectedProxy": "Selected Proxy",
"settings.proxy.searchEngine": "Search Engine",
"settings.proxy.openPageWith": "Open With",
"settings.proxy.openPageWith.embed": "Embed",
"settings.proxy.openPageWith.newTab": "New Tab",
"settings.proxy.searxngURL": "Searx URL",
"settings.proxy.bareURL": "Bare URL",
"settings.customization": "Customization",
"settings.customization.theme": "Theme",
"settings.customization.theme.Alu": "Alu",
"settings.customization.theme.Macchiato": "Macchiato",
"settings.customization.theme.Mocha": "Mocha",
"settings.customization.language": "Language",
"settings.customization": "Customization",
"settings.customization.theme": "Theme",
"settings.customization.theme.Alu": "Alu",
"settings.customization.theme.Macchiato": "Macchiato",
"settings.customization.theme.Mocha": "Mocha",
"settings.customization.language": "Language",
"settings.cloaking": "Cloaking",
"settings.cloaking.subtext": "Change how your tab looks...",
"settings.cloaking": "Cloaking",
"settings.cloaking.subtext": "Change how your tab looks...",
"settings.credits": "Credits",
"settings.credits.mochaandmacchiatothemes": "Mocha & Macchiato Themes",
"settings.credits.japaneseTranslations": "Japanese Translations"
}
"settings.credits": "Credits",
"settings.credits.mochaandmacchiatothemes": "Mocha & Macchiato Themes",
"settings.credits.japaneseTranslations": "Japanese Translations"
}

View file

@ -1,46 +1,47 @@
{
"ultraviolet": "ウルトラバイオレット",
"pages.home": "ホーム | アルー",
"pages.games": "ゲーム | アルー",
"pages.settings": "設定 | アルー",
"ultraviolet": "ウルトラバイオレット",
"nav.brand": "アルー",
"nav.games": "ゲーム",
"nav.settings": "設定",
"pages.home": "ホーム | アルー",
"pages.games": "ゲーム | アルー",
"pages.settings": "設定 | アルー",
"menu.welcome": "アルーにようこそ",
"menu.search": "検索...",
"nav.brand": "アルー",
"nav.games": "ゲーム",
"nav.settings": "設定",
"footer.brand": "アルー",
"footer.madeWithLove": "wearrによる❤で作られました",
"footer.services": "サービス",
"footer.socials": "ソーシャル",
"footer.aluProject": "アループロジェクト",
"menu.welcome": "アルーにようこそ",
"menu.search": "検索...",
"games.title": "ゲーム",
"footer.brand": "アルー",
"footer.madeWithLove": "wearrによる❤で作られました",
"footer.poweredBy": "「ウルトラバイオレット」による駆動",
"footer.services": "サービス",
"footer.socials": "ソーシャル",
"footer.aluProject": "アループロジェクト",
"settings.title": "設定",
"settings.proxy": "プロキシ",
"settings.proxy.selectedProxy": "選択されたプロキシ",
"settings.proxy.searchEngine": "検索エンジン",
"settings.proxy.openPageWith": "開く",
"settings.proxy.searxngURL": "SearxのURL",
"settings.proxy.openPageWith.embed": "埋め込み",
"settings.proxy.openPageWith.newTab": "新しいタブ",
"settings.proxy.bareURL": "BareのURL",
"games.title": "ゲーム",
"settings.customization": "カスタマイズ",
"settings.customization.theme": "テーマ",
"settings.customization.theme.Alu": "アルー",
"settings.customization.theme.Macchiato": "マキアート",
"settings.customization.theme.Mocha": "モカ",
"settings.customization.language": "言語",
"settings.cloaking": "クローキング",
"settings.cloaking.subtext": "タブの外観を変更します...",
"settings.title": "設定",
"settings.proxy": "プロキシ",
"settings.proxy.selectedProxy": "選択されたプロキシ",
"settings.proxy.searchEngine": "検索エンジン",
"settings.proxy.openPageWith": "開く",
"settings.proxy.searxngURL": "SearxのURL",
"settings.proxy.openPageWith.embed": "埋め込み",
"settings.proxy.openPageWith.newTab": "新しいタブ",
"settings.proxy.bareURL": "BareのURL",
"settings.credits": "クレジット",
"settings.credits.mochaandmacchiatothemes": "モカとマキアートテーマ",
"settings.credits.japaneseTranslations": "日本語翻訳"
}
"settings.customization": "カスタマイズ",
"settings.customization.theme": "テーマ",
"settings.customization.theme.Alu": "アルー",
"settings.customization.theme.Macchiato": "マキアート",
"settings.customization.theme.Mocha": "モカ",
"settings.customization.language": "言語",
"settings.cloaking": "クローキング",
"settings.cloaking.subtext": "タブの外観を変更します...",
"settings.credits": "クレジット",
"settings.credits.mochaandmacchiatothemes": "モカとマキアートテーマ",
"settings.credits.japaneseTranslations": "日本語翻訳"
}

View file

@ -1,9 +1,9 @@
import en from './en.json';
import jp from './jp.json';
import en from "./en.json";
import jp from "./jp.json";
export const defaultLang = "en";
export const defaultLang = 'en';
export const ui = {
en,
jp
};
en,
jp,
};

View file

@ -1,14 +1,14 @@
import { ui, defaultLang } from './ui';
import { ui, defaultLang } from "./ui";
export function getLangFromUrl(url: URL) {
// comma lol
const [, lang] = url.pathname.split('/');
const [, lang] = url.pathname.split("/");
if (lang in ui) return lang as keyof typeof ui;
return defaultLang;
}
export function useTranslations(lang: keyof typeof ui) {
return function t(key: keyof typeof ui[typeof defaultLang]) {
return function t(key: keyof (typeof ui)[typeof defaultLang]) {
return ui[lang][key] || ui[defaultLang][key];
}
}
};
}

262
src/json/games.json Normal file
View file

@ -0,0 +1,262 @@
{
"1v1.lol": {
"name": "1v1.lol",
"image": "/games/1v1.lol/logo.png",
"slug": "1v1.lol"
},
"2048": {
"name": "2048",
"image": "/games/2048/logo.png",
"slug": "2048"
},
"backrooms": {
"name": "The Backrooms",
"image": "/games/backrooms/logo.png",
"slug": "backrooms"
},
"baldi": {
"name": "Baldi's Basics",
"image": "/games/baldi/logo.png",
"slug": "baldi"
},
"cannon-basketball-4": {
"name": "Cannon Basketball 4",
"image": "/games/cannon-basketball-4/logo.png",
"slug": "cannon-basketball-4"
},
"cell-machine": {
"name": "Cell Machine",
"image": "/games/cell-machine/logo.png",
"slug": "cell-machine"
},
"chrome-dino": {
"name": "Chrome Dino",
"image": "/games/chrome-dino/logo.png",
"slug": "chrome-dino"
},
"cookie-clicker": {
"name": "Cookie Clicker",
"image": "/games/cookie-clicker/logo.png",
"slug": "cookie-clicker"
},
"csgo-clicker": {
"name": "CSGO Clicker",
"image": "/games/csgo-clicker/logo.png",
"slug": "csgo-clicker"
},
"ctr": {
"name": "Cut The Rope",
"image": "/games/ctr/logo.png",
"slug": "ctr"
},
"ctr-holiday": {
"name": "Cut The Rope - Holiday",
"image": "/games/ctr-holiday/logo.png",
"slug": "ctr-holiday"
},
"ctr-tr": {
"name": "Cut The Rope - Time Travel",
"image": "/games/ctr-tr/logo.png",
"slug": "ctr-tr"
},
"death-run-3d": {
"name": "Death Run 3D",
"image": "/games/death-run-3d/logo.png",
"slug": "death-run-3d"
},
"doge-miner": {
"name": "Doge Miner",
"image": "/games/doge-miner/logo.png",
"slug": "doge-miner"
},
"doodle-jump": {
"name": "Doodle Jump",
"image": "/games/doodle-jump/logo.png",
"slug": "doodle-jump"
},
"doom": {
"name": "Doom",
"image": "/games/doom/logo.png",
"slug": "doom"
},
"draw-the-hill": {
"name": "Draw The Hill",
"image": "/games/draw-the-hill/logo.png",
"slug": "draw-the-hill"
},
"evil-glitch": {
"name": "Evil Glitch",
"image": "/games/evil-glitch/logo.png",
"slug": "evil-glitch"
},
"fall-boys": {
"name": "Fall Boys",
"image": "/games/fall-boys/logo.png",
"slug": "fall-boys"
},
"firewater": {
"name": "Fireboy and Watergirl",
"image": "/games/firewater/logo.png",
"slug": "firewater"
},
"firewater2": {
"name": "Fireboy and Watergirl 2",
"image": "/games/firewater2/logo.png",
"slug": "firewater2"
},
"fnaf": {
"name": "Five Nights at Freddy's",
"image": "/games/fnaf/logo.png",
"slug": "fnaf"
},
"fnaf2": {
"name": "Five Nights at Freddy's 2",
"image": "/games/fnaf2/logo.png",
"slug": "fnaf2"
},
"fnaf3": {
"name": "Five Nights at Freddy's 3",
"image": "/games/fnaf3/logo.png",
"slug": "fnaf3"
},
"fnaf4": {
"name": "Five Nights at Freddy's 4",
"image": "/games/fnaf4/logo.png",
"slug": "fnaf4"
},
"game-inside": {
"name": "Game Inside",
"image": "/games/game-inside/logo.png",
"slug": "game-inside"
},
"google-snake": {
"name": "Google Snake",
"image": "/games/google-snake/logo.png",
"slug": "google-snake"
},
"grindcraft": {
"name": "Grindcraft",
"image": "/games/grindcraft/logo.png",
"slug": "grindcraft"
},
"idle-breakout": {
"name": "Idle Breakout",
"image": "/games/idle-breakout/logo.png",
"slug": "idle-breakout"
},
"just-one-boss": {
"name": "Just One Boss",
"image": "/games/just-one-boss/logo.png",
"slug": "just-one-boss"
},
"line-rider": {
"name": "Line Rider",
"image": "/games/line-rider/logo.png",
"slug": "line-rider"
},
"minecraft-classic": {
"name": "Minecraft Classic",
"image": "/games/minecraft-classic/logo.png",
"slug": "minecraft-classic"
},
"minesweeper": {
"name": "Minesweeper",
"image": "/games/minesweeper/logo.png",
"slug": "minesweeper"
},
"moto-x3m": {
"name": "Moto X3M",
"image": "/games/moto-x3m/logo.png",
"slug": "moto-x3m"
},
"moto-x3m-pool": {
"name": "Moto X3M Pool Party",
"image": "/games/moto-x3m-pool/logo.png",
"slug": "moto-x3m-pool"
},
"moto-x3m-spooky": {
"name": "Moto X3M Spooky Land",
"image": "/games/moto-x3m-spooky/logo.png",
"slug": "moto-x3m-spooky"
},
"moto-x3m-winter": {
"name": "Moto X3M Winter",
"image": "/games/moto-x3m-winter/logo.png",
"slug": "moto-x3m-winter"
},
"osu": {
"name": "osu!",
"image": "/games/osu/logo.png",
"slug": "osu"
},
"retro-bowl": {
"name": "Retro Bowl",
"image": "/games/retro-bowl/logo.png",
"slug": "retro-bowl"
},
"slope": {
"name": "Slope",
"image": "/games/slope/logo.png",
"slug": "slope"
},
"sm64": {
"name": "Super Mario 64",
"image": "/games/sm64/logo.png",
"slug": "sm64"
},
"solitaire": {
"name": "Solitaire",
"image": "/games/solitaire/logo.png",
"slug": "solitaire"
},
"superhot": {
"name": "Superhot",
"image": "/games/superhot/logo.png",
"slug": "superhot"
},
"there-is-no-game": {
"name": "There is No Game",
"image": "/games/there-is-no-game/logo.png",
"slug": "there-is-no-game"
},
"ul6": {
"name": "Ultima 6",
"image": "/games/ul6/logo.png",
"slug": "ul6"
},
"vex3": {
"name": "Vex 3",
"image": "/games/vex3/logo.png",
"slug": "vex3"
},
"vex4": {
"name": "Vex 4",
"image": "/games/vex4/logo.png",
"slug": "vex4"
},
"vex5": {
"name": "Vex 5",
"image": "/games/vex5/logo.png",
"slug": "vex5"
},
"vex6": {
"name": "Vex 6",
"image": "/games/vex6/logo.png",
"slug": "vex6"
},
"worlds-hardest-game": {
"name": "World's Hardest Game",
"image": "/games/worlds-hardest-game/logo.png",
"slug": "worlds-hardest-game"
},
"worlds-hardest-game-2": {
"name": "World's Hardest Game 2",
"image": "/games/worlds-hardest-game-2/logo.png",
"slug": "worlds-hardest-game-2"
},
"you-are-bezos": {
"name": "You are Bezos",
"image": "/games/you-are-bezos/logo.png",
"slug": "you-are-bezos"
}
}

View file

@ -1,172 +1,192 @@
---
import { ViewTransitions } from 'astro:transitions';
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
import ThemeLoader from '../components/ThemeLoader.astro';
import CloakLoader from '../components/CloakLoader.astro';
import WelcomeLogging from '../components/WelcomeLogging.astro';
import { ViewTransitions } from "astro:transitions";
import Header from "../components/Header.astro";
import Footer from "../components/Footer.astro";
import ThemeLoader from "../components/ThemeLoader.astro";
import CloakLoader from "../components/CloakLoader.astro";
import WelcomeLogging from "../components/WelcomeLogging.astro";
import UVRegistrar from "../components/ProxyRegistrar.astro";
type Preload = {
"href": string
"as": string
}
href: string;
as: string;
};
interface Props {
title: string;
title: string;
optionalPreloads?: Preload[];
}
const { title, optionalPreloads } = Astro.props;
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="description" content="Astro description" />
<meta name="viewport" content="width=device-width" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Varela+Round&display=swap" rel="stylesheet" as="style">
{optionalPreloads?.map((item) => {
return <link rel="preload" href={item.href} as={item.as} />
})}
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
<ViewTransitions />
<ThemeLoader />
<head>
<ThemeLoader transition:persist />
<CloakLoader transition:persist />
</head>
<body>
<Header></Header>
<slot transition:animate={"fade"} />
<WelcomeLogging />
<Footer></Footer>
</body>
<meta charset="UTF-8" />
<meta name="description" content="Astro description" />
<meta name="viewport" content="width=device-width" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Varela+Round&display=swap"
rel="stylesheet"
as="style"
/>
{
optionalPreloads?.map((item) => {
return <link rel="preload" href={item.href} as={item.as} />;
})
}
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
<ViewTransitions />
</head>
<body>
<Header />
<slot transition:animate={"fade"} />
<WelcomeLogging />
<UVRegistrar />
<Footer />
<style is:global>
:root {
--background-color: #080808;
--background-highlight: #252525;
--accent-color: #6b00c9;
--accent-color-brighter: #7e00e0;
--text-color: #fff;
--text-color-accent: #c7c7c7;
--dropdown-background-color: #1e1e1e;
}
* {
box-sizing: border-box;
}
body {
opacity: 0;
animation: fadeIn ease 0.4s forwards;
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
[data-theme="mocha"] {
/* Catppucin Mocha theme */
--background-color: #1e1e2e;
--background-highlight: #45475a;
--accent-color: #181825;
--accent-color-brighter: #242539;
--text-color: #cdd6f4;
--text-color-accent: #bac2de;
--dropdown-background-color: #32324e;
}
[data-theme="macchiato"] {
/* Catppuccin Macchiato Theme */
--background-color: #24273a;
--background-highlight: #494d64;
--accent-color: #1e2030;
--accent-color-brighter: #2a2d42;
--text-color: #cad3f5;
--text-color-accent: #b8c0e0;
--dropdown-background-color: #323550;
}
[data-theme="rose_pine"] {
/* Rosé Pine Theme */
--background-color: #191724;
--background-highlight: #1f1d2e;
--accent-color: #26233a;
--accent-color-brighter: #2e2b4a;
--text-color: #e0def4;
--text-color-accent: #c7c5e0;
--dropdown-background-color: #1f1d2e;
}
body,
html {
margin: 0;
padding: 0;
height: 100%;
font-family: "Varela Round", sans-serif;
}
body {
background-color: var(--background-color);
max-width: 100vw;
margin: 0;
display: flex;
flex-direction: column;
min-height: 100vh;
}
body > * {
opacity: 1;
}
.title-text {
color: var(--text-color);
text-align: center;
font-weight: 400;
}
::-webkit-scrollbar {
display: none;
}
.main-content {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width: 100%;
height: 60vh;
}
#proxy-frame {
display: block;
position: absolute;
z-index: 1000;
width: 100vw;
height: 95vh;
top: 5vh;
left: 0;
border: none;
background-color: var(--background-color);
transition: opacity 250ms ease-in-out;
pointer-events: none;
}
#proxy-frame {
opacity: 0;
}
.top-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 1rem;
height: 5vh;
background-color: var(--accent-color);
color: var(--text-color);
position: fixed;
top: 0;
left: 0;
width: 100vw;
z-index: 100;
transition: opacity 350ms ease-in-out;
pointer-events: none;
}
.close-button {
padding: 5px;
padding-inline: 40px;
border: none;
background-color: var(--background-highlight);
color: var(--text-color-accent);
border-radius: 15px;
cursor: pointer;
}
</style>
</body>
</html>
<style is:global>
* {
box-sizing: border-box;
}
body {
opacity: 0;
animation: fadeIn ease 0.15s forwards;
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
:root {
--background-color: #080808;
--background-highlight: #252525;
--accent-color: #6b00c9;
--accent-color-brighter: #8800ff;
--text-color: #fff;
--text-color-accent: #c7c7c7;
--dropdown-background-color: #1e1e1e;
}
[data-theme="mocha"] {
/* Catppucin Mocha theme */
--background-color: #1e1e2e;
--background-highlight: #45475a;
--accent-color: #181825;
--accent-color-brighter: #242539;
--text-color: #cdd6f4;
--text-color-accent: #bac2de;
--dropdown-background-color: #32324e;
}
[data-theme="macchiato"] {
/* Catppuccin Macchiato Theme */
--background-color: #24273a;
--background-highlight: #494d64;
--accent-color: #1e2030;
--accent-color-brighter: #525571;
--text-color: #cad3f5;
--text-color-accent: #b8c0e0;
--dropdown-background-color: #323550;
}
body, html {
margin: 0;
padding: 0;
height: 100%;
font-family: 'Varela Round', sans-serif;
}
body {
background-color: var(--background-color);
max-width: 100vw;
margin: 0;
display: flex;
flex-direction: column;
min-height: 100vh;
}
body > * {
opacity: 1;
}
.title-text {
color: var(--text-color);
text-align: center;
font-weight: 400;
}
::-webkit-scrollbar {
display: none;
}
.main-content {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width: 100%;
height: 60vh;
}
#proxy-frame {
display: block;
position: absolute;
z-index: 1000;
width: 100vw;
height: 95vh;
top: 5vh;
border: none;
background-color: var(--background-color);
transition: opacity 250ms ease-in-out;
pointer-events: none;
}
#proxy-frame {
opacity: 0;
}
.top-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 1rem;
height: 5vh;
background-color: var(--accent-color);
color: var(--text-color);
position: fixed;
top: 0;
left: 0;
width: 100vw;
z-index: 100;
transition: opacity 350ms ease-in-out;
pointer-events: none;
}
.close-button {
padding: 5px;
padding-inline: 40px;
border: none;
background-color: var(--background-highlight);
color: var(--text-color-accent);
border-radius: 15px;
cursor: pointer;
}
</style>

View file

@ -3,14 +3,14 @@ import Layout from "../layouts/Layout.astro";
---
<Layout title="404 | Alu">
<div class="main-content error-page">
<h1>404!</h1>
<p>The content you have requested could not be found!</p>
</div>
<div class="main-content error-page">
<h1>404!</h1>
<p>The content you have requested could not be found!</p>
</div>
</Layout>
<style>
.error-page {
color: white
}
</style>
.error-page {
color: white;
}
</style>

View file

@ -1,80 +1,31 @@
---
import Layout from "../../layouts/Layout.astro";
import games from "../../json/games.json";
import GameItem from "../../components/GameItem.astro";
import { getLangFromUrl, useTranslations } from "../../i18n/utils"
import { getLangFromUrl, useTranslations } from "../../i18n/utils";
const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
export function getStaticPaths () {
return [
{params: {lang: 'en'}},
{params: {lang: 'jp'}},
];
export function getStaticPaths() {
return [{ params: { lang: "en" } }, { params: { lang: "jp" } }];
}
---
<Layout title={t("pages.games")}>
<h1 class="title-text">{t("games.title")}</h1>
<div class="grid">
<GameItem name="1v1.lol" image="/games/1v1.lol/logo.png"></GameItem>
<GameItem name="2048" image="/games/2048/logo.png"></GameItem>
<GameItem name="The Backrooms" image="/games/backrooms/logo.png" slugName="backrooms"></GameItem>
<GameItem name="Baldi's Basics" image="/games/baldi/logo.png" slugName="baldi"></GameItem>
<GameItem name="Cannon Basketball 4" image="/games/cannon-basketball-4/logo.png" slugName="cannon-basketball-4"></GameItem>
<GameItem name="Cell Machine" image="/games/cell-machine/logo.png" slugName="cell-machine"></GameItem>
<GameItem name="Chrome Dino" image="/games/chrome-dino/logo.png" slugName="chrome-dino"></GameItem>
<GameItem name="Cookie Clicker" image="/games/cookie-clicker/logo.png" slugName="cookie-clicker"></GameItem>
<GameItem name="CSGO Clicker" image="/games/csgo-clicker/logo.png" slugName="csgo-clicker"></GameItem>
<GameItem name="Cut The Rope" image="/games/ctr/logo.png" slugName="ctr"></GameItem>
<GameItem name="Cut The Rope - Holiday" image="/games/ctr-holiday/logo.png" slugName="ctr-holiday"></GameItem>
<GameItem name="Cut The Rope - Time Travel" image="/games/ctr-tr/logo.png" slugName="ctr-tr"></GameItem>
<GameItem name="Death Run 3D" image="/games/death-run-3d/logo.png" slugName="death-run-3d"></GameItem>
<GameItem name="Doge Miner" image="/games/doge-miner/logo.png" slugName="doge-miner"></GameItem>
<GameItem name="Doodle Jump" image="/games/doodle-jump/logo.png" slugName="doodle-jump"></GameItem>
<GameItem name="Doom" image="/games/doom/logo.png" slugName="doom"></GameItem>
<GameItem name="Draw The Hill" image="/games/draw-the-hill/logo.png" slugName="draw-the-hill"></GameItem>
<GameItem name="Evil Glitch" image="/games/evil-glitch/logo.png" slugName="evil-glitch"></GameItem>
<GameItem name="Fall Boys" image="/games/fall-boys/logo.png" slugName="fall-boys"></GameItem>
<GameItem name="Fireboy and Watergirl" image="/games/firewater/logo.png" slugName="firewater"></GameItem>
<GameItem name="Fireboy and Watergirl 2" image="/games/firewater2/logo.png" slugName="firewater2"></GameItem>
<GameItem name="Five Nights at Freddy's" image="/games/fnaf/logo.png" slugName="fnaf"></GameItem>
<GameItem name="Five Nights at Freddy's 2" image="/games/fnaf2/logo.png" slugName="fnaf2"></GameItem>
<GameItem name="Five Nights at Freddy's 3" image="/games/fnaf3/logo.png" slugName="fnaf3"></GameItem>
<GameItem name="Five Nights at Freddy's 4" image="/games/fnaf4/logo.png" slugName="fnaf4"></GameItem>
<GameItem name="Game Inside" image="/games/game-inside/logo.png" slugName="game-inside"></GameItem>
<GameItem name="Google Snake" image="/games/google-snake/logo.png" slugName="google-snake"></GameItem>
<GameItem name="Grindcraft" image="/games/grindcraft/logo.png" slugName="grindcraft"></GameItem>
<GameItem name="Idle Breakout" image="/games/idle-breakout/logo.png" slugName="idle-breakout"></GameItem>
<GameItem name="Just One Boss" image="/games/just-one-boss/logo.png" slugName="just-one-boss"></GameItem>
<GameItem name="Line Rider" image="/games/line-rider/logo.png" slugName="line-rider"></GameItem>
<GameItem name="Minecraft Classic" image="/games/minecraft-classic/logo.png" slugName="minecraft-classic"></GameItem>
<GameItem name="Minesweeper" image="/games/minesweeper/logo.png" slugName="minesweeper"></GameItem>
<GameItem name="Moto X3M" image="/games/moto-x3m/logo.png" slugName="moto-x3m"></GameItem>
<GameItem name="Moto X3M Pool Party" image="/games/moto-x3m-pool/logo.png" slugName="moto-x3m-pool"></GameItem>
<GameItem name="Moto X3M Spooky Land" image="/games/moto-x3m-spooky/logo.png" slugName="moto-x3m-spooky"></GameItem>
<GameItem name="Moto X3M Winter" image="/games/moto-x3m-winter/logo.png" slugName="moto-x3m-winter"></GameItem>
<GameItem name="osu!" image="/games/osu/logo.png" slugName="osu"></GameItem>
<GameItem name="Retro Bowl" image="/games/retro-bowl/logo.png" slugName="retro-bowl"></GameItem>
<GameItem name="Slope" image="/games/slope/logo.png" slugName="slope"></GameItem>
<GameItem name="Super Mario 64" image="/games/sm64/logo.png" slugName="sm64"></GameItem>
<GameItem name="Solitaire" image="/games/solitaire/logo.png" slugName="solitaire"></GameItem>
<GameItem name="Superhot" image="/games/superhot/logo.png" slugName="superhot"></GameItem>
<GameItem name="There is No Game" image="/games/there-is-no-game/logo.png" slugName="there-is-no-game"></GameItem>
<GameItem name="Ultima 6" image="/games/ul6/logo.png" slugName="ul6"></GameItem>
<GameItem name="Vex 3" image="/games/vex3/logo.png" slugName="vex3"></GameItem>
<GameItem name="Vex 4" image="/games/vex4/logo.png" slugName="vex4"></GameItem>
<GameItem name="Vex 5" image="/games/vex5/logo.png" slugName="vex5"></GameItem>
<GameItem name="Vex 6" image="/games/vex6/logo.png" slugName="vex6"></GameItem>
<GameItem name="World's Hardest Game" image="/games/worlds-hardest-game/logo.png" slugName="worlds-hardest-game"></GameItem>
<GameItem name="World's Hardest Game 2" image="/games/worlds-hardest-game-2/logo.png" slugName="worlds-hardest-game-2"></GameItem>
<GameItem name="You are Bezos" image="/games/you-are-bezos/logo.png" slugName="you-are-bezos"></GameItem>
</div>
<h1 class="title-text">{t("games.title")}</h1>
<div class="grid">
{
Object.keys(games).map((key) => {
const game = (games as any)[key];
return <GameItem name={game.name} image={game.image} slugName={game.slug} />;
})
}
</div>
</Layout>
<style>
.grid {
.grid {
color: var(--text-color);
margin-top: -10px;
height: max-content;
@ -86,18 +37,18 @@ export function getStaticPaths () {
justify-content: center;
gap: 1rem;
margin-left: 20px;
}
@media only screen and (max-width: 973px) {
.grid {
grid-template-columns: repeat(3, 1fr);
padding: 0;
}
}
}
@media only screen and (max-width: 467px) {
@media only screen and (max-width: 973px) {
.grid {
grid-template-columns: repeat(2, 1fr);
grid-template-columns: repeat(3, 1fr);
padding: 0;
}
}
</style>
}
@media only screen and (max-width: 467px) {
.grid {
grid-template-columns: repeat(2, 1fr);
}
}
</style>

View file

@ -1,75 +1,70 @@
---
import Layout from "../../layouts/Layout.astro";
import UVRegistrar from "../../components/UVRegistrar.astro";
import { getLangFromUrl, useTranslations } from "../../i18n/utils"
import { getLangFromUrl, useTranslations } from "../../i18n/utils";
const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
export function getStaticPaths () {
return [
{params: {lang: 'en'}},
{params: {lang: 'jp'}},
];
export function getStaticPaths() {
return [{ params: { lang: "en" } }, { params: { lang: "jp" } }];
}
---
<Layout title={t("pages.home")}>
<div class="main-content">
<h1 class="title-text">{t("menu.welcome")}</h1>
<div class="main-content">
<h1 class="title-text">{t("menu.welcome")}</h1>
<form class="url-input-form" id="url-input-form">
<input class="url-input" type="text" placeholder={t("menu.search")}>
<input class="url-input" type="text" placeholder={t("menu.search")} />
<div id="loading-content">Loading...</div>
<div id="top-bar"></div>
<iframe title="proxy-iframe" id="proxy-frame"></iframe>
</form>
</div>
<UVRegistrar></UVRegistrar>
</div>
</Layout>
<style>
form {
width: 30%;
}
form {
width: 30%;
}
.title-text {
font-size: 38px;
}
.title-text {
font-size: 38px;
}
.url-input-form {
border: none;
padding: 0;
}
.url-input-form {
border: none;
padding: 0;
}
.url-input {
display: block;
background: transparent url("/img/search.svg") no-repeat 13px center;
}
.url-input {
display: block;
background: transparent url("/img/search.svg") no-repeat 13px center;
}
.url-input {
background-color: #080808;
color: #D8DEE9;
border: 3px solid white;
border-radius: 30px;
padding: 15px;
width: 100%;
text-align: center;
transition: 400ms ease-out;
outline: none;
font-family: 'Varela Round', sans-serif;
font-size: 16px;
}
.url-input {
background-color: var(--dropdown-background-color);
color: var(--text-color);
border: 3px solid var(--text-color);
border-radius: 30px;
padding: 15px;
width: 100%;
text-align: center;
transition: 400ms ease-out;
outline: none;
font-family: "Varela Round", sans-serif;
font-size: 16px;
}
.url-input::placeholder {
color: #D8DEE9;
}
.url-input::placeholder {
color: var(--text-color);
}
#loading-content {
color: white;
padding: 8px;
position: relative;
opacity: 0;
transition: 250ms ease-in-out;
text-align: center;
}
</style>
#loading-content {
color: var(--text-color);
padding: 8px;
position: relative;
opacity: 0;
transition: 250ms ease-in-out;
text-align: center;
}
</style>

View file

@ -2,25 +2,22 @@
import SettingsTablist from "../../components/SettingsTablist.astro";
import Layout from "../../layouts/Layout.astro";
import { getLangFromUrl, useTranslations } from "../../i18n/utils"
import { getLangFromUrl, useTranslations } from "../../i18n/utils";
const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
export function getStaticPaths () {
return [
{params: {lang: 'en'}},
{params: {lang: 'jp'}},
];
export function getStaticPaths() {
return [{ params: { lang: "en" } }, { params: { lang: "jp" } }];
}
---
<Layout title={t("pages.settings")}>
<h1 class="title-text">Settings</h1>
<SettingsTablist />
<h1 class="title-text">Settings</h1>
<SettingsTablist />
</Layout>
<style>
h1 {
font-size: 40px;
}
</style>
h1 {
font-size: 40px;
}
</style>

View file

@ -1,6 +1,5 @@
---
import { ViewTransitions } from "astro:transitions";
---
<!doctype html>
@ -13,24 +12,23 @@ import { ViewTransitions } from "astro:transitions";
let currentLang = localStorage.getItem("alu__selectedLanguage");
if (currentLang) {
try {
let parsed = JSON.parse(currentLang).value
let parsed = JSON.parse(currentLang).value;
switch (parsed) {
case "en":
window.location.href = "/en/";
break;
window.location.href = "/en/";
break;
case "jp":
window.location.href = "/jp/";
break;
window.location.href = "/jp/";
break;
default:
window.location.href = "/en/";
break;
}
window.location.href = "/en/";
break;
}
} catch {
localStorage.clear();
window.location.reload();
}
}
</script>
</head>
<body></body>

View file

@ -1,3 +1,6 @@
{
"extends": "astro/tsconfigs/strict"
}
"extends": "astro/tsconfigs/strict",
"compilerOptions": {
"verbatimModuleSyntax": false
}
}