i18n is so weird brooo

This commit is contained in:
rift 2024-07-22 14:52:42 -05:00
parent d82bee3150
commit 781a31d3bb
119 changed files with 8174 additions and 14035 deletions

View file

@ -1,5 +0,0 @@
node_modules/
.vscode
npm-debug.log
yarn-error.log
.github/

1
.env
View file

@ -1 +0,0 @@
MASQR=1

35
.gitignore vendored
View file

@ -1,25 +1,24 @@
# Logs
logs
*.log
# build output
dist/
# generated types
.astro/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
package-lock.json
# environment variables
.env
.env.production
dist-ssr
dist
*.local
# Editor directories and files
.idea
# macOS-specific files
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
~/
# jetbrains setting folder
.idea/

View file

@ -1,9 +0,0 @@
# Build artifacts
dist/
# Other things that don't need to be reformatted
public/uv/
public/dynamic/
pnpm-lock.yaml
package-lock.json
package.json

View file

@ -1,8 +0,0 @@
{
"singleQuote": false,
"endOfLine": "crlf",
"tabWidth": 2,
"useTabs": false,
"trailingComma": "none",
"plugins": ["prettier-plugin-tailwindcss"]
}

4
.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,4 @@
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}

11
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}

12
.vscode/settings.json vendored
View file

@ -1,12 +0,0 @@
{
"editor.tabSize": 2,
"editor.insertSpaces": true,
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"files.autoSave": "afterDelay",
"files.autoSaveDelay": 0,
"css.lint.unknownAtRules": "ignore",
"editor.linkedEditing": true,
"css.lint.unknownProperties": "ignore"
}

View file

@ -1,40 +0,0 @@
<!doctype html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html {
color-scheme: light dark;
}
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>
If you see this page, the nginx web server is successfully installed and
working. Further configuration is required. If you are expecting another
page, please check your network or
<a href="/" id="rcheck"><b>Refresh this page</b></a>
</p>
<p>
For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br />
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.
</p>
<p><em>Thank you for using nginx.</em></p>
<script>
if (!localStorage["auth"] && new URL(document.all.rcheck.href).password) {
window.location.reload();
localStorage["auth"] = 1;
}
</script>
</body>
</html>

View file

@ -1,14 +0,0 @@
FROM node:21-alpine
WORKDIR /app
COPY package*.json .
COPY . .
RUN npm i -g pnpm
RUN pnpm install
RUN pnpm run build
EXPOSE 8080
CMD ["pnpm", "start"]

121
README.md
View file

@ -1,67 +1,54 @@
# Nebula
NebulaWeb is an official flagship of Nebula Services and Nebula Developer Labs. NebulaWeb is a stunning, sleek, and functional web-proxy with support for thousands of popular sites. With NebulaWeb, the sky is the limit.
![license](https://img.shields.io/badge/License-GNU%20AGPL%20v3-blue)
## Features
- Stunning and highly functional UI
- 3 different backend proxies
- Hides your IP from sites
- [List of officially supported sites](https://github.com/NebulaServices/Nebula/blob/dev/docs/officially-supported-sites.md)
- Full mobile support
- `about:blank` cloaking)
# Deployment
Table of contents
- Deployment
---
## Deployment
Run these commands on your server:
`git clone https://github.com/NebulaServices/Nebula.git`
`pnpm i`
`npm run bstart`
You may also need to run `npm i -g pnpm tsx`
Thanks for using Nebula!
## Tech Stack
- TypeScript, Tailwind
- Fastify
- TSX
- Framer motion
- react-i18next
- Ultraviolet (proxy)
- Dynamic (proxy)
- Rammerhead (proxy)
- TompHTTP Bare Server Node
## Support
For support, join our discord: discord.gg/unblocker
## Demo
[Click here to see a demo of Nebula](https://nebulaproxy.io/)
## Acknowledgements
- [UV (one of the proxies we use)](https://github.com/titaniumnetwork-dev/Ultraviolet)
- [Dynamic (one of the proxies we use)](https://github.com/NebulaServices/Dynamic)
- [Rammerhead (one of the proxies we use)](https://github.com/binary-person/rammerhead)
- [Bare Server Node](https://github.com/tomphttp/bare-server-node)
- [Catppuccin (for the themes)](https://github.com/catppuccin/catppuccin)
## License
(Nebula's license is now GNU AGPL V3 as of v7.10)
Copyright Nebula Services 2021 - Present
<br>
This project uses the AGLP GNU V3 license.
# Astro Starter Kit: Basics
```sh
npm create astro@latest -- --template basics
```
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/basics)
[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/basics)
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/basics/devcontainer.json)
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
![just-the-basics](https://github.com/withastro/astro/assets/2244813/a0a5533c-a856-4198-8470-2d67b1d7c554)
## 🚀 Project Structure
Inside of your Astro project, you'll see the following folders and files:
```text
/
├── public/
│ └── favicon.svg
├── src/
│ ├── components/
│ │ └── Card.astro
│ ├── layouts/
│ │ └── Layout.astro
│ └── pages/
│ └── index.astro
└── package.json
```
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
Any static assets, like images, can be placed in the `public/` directory.
## 🧞 Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :------------------------ | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:4321` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro -- --help` | Get help using the Astro CLI |
## 👀 Want to learn more?
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).

9
astro.config.mjs Normal file
View file

@ -0,0 +1,9 @@
import { defineConfig } from 'astro/config';
import tailwind from "@astrojs/tailwind";
import icon from "astro-icon";
// https://astro.build/config
export default defineConfig({
integrations: [tailwind(), icon()]
});

View file

@ -1,13 +0,0 @@
version: "3"
services:
nebula:
image: nebula:latest
build:
context: .
dockerfile: Dockerfile
container_name: nebula
restart: unless-stopped
ports:
# Host:Container (DO NOT MODIFY THE CONTAINER PORT)
- "8081:8080"

7
framer-motion.d.ts vendored
View file

@ -1,7 +0,0 @@
import * as React from "preact/compat";
declare module "framer-motion" {
export interface AnimatePresenceProps {
children?: React.ReactNode;
}
}

View file

@ -1,34 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="/transports/bareTransport.js" defer></script>
<script src="/uv/uv.bundle.js" defer></script>
<script src="/uv/uv.config.js" defer></script>
<script src="/dynamic/dynamic.config.js" defer></script>
<script src="/localforage/localforage.min.js" defer></script>
</head>
<body style="margin: 0">
<div id="app"></div>
<script type="module" src="/src/index.tsx"></script>
<script>
if (
new URL(Object.assign(document.createElement("a"), { href: "/" }).href)
.password
)
window.location.href = window.location.href;
</script>
<script>
try {
if (
!localStorage["auth"] &&
new URL(document.all.rcheck.href).password
) {
window.location.reload();
localStorage["auth"] = 1;
}
} catch {}
</script>
</body>
</html>

View file

@ -1,75 +0,0 @@
import fp from "fastify-plugin";
import fs from "fs";
const failureFile = fs.readFileSync("Checkfailed.html", "utf8");
const LICENSE_SERVER_URL = "https://license.mercurywork.shop/validate?license=";
const whiteListedDomain = ["nebulaproxy.io"];
async function licenseCheck(req, pass) {
try {
const resp = await fetch(
`${LICENSE_SERVER_URL}${pass}&host=${req.headers.host}`
);
const data = await resp.json();
if (data.status === "License valid") {
return true;
} else {
return false;
}
} catch {
return false;
}
}
const plugin = (fastify, opts, done) => {
fastify.addHook("onRequest", function (req, reply, next) {
if (
req.cookies.authcheck === "true" ||
whiteListedDomain.includes(req.headers.host)
) {
return next();
}
const authHeader = req.headers.authorization;
if (req.cookies.refreshcheck != "true") {
reply
.setCookie("refreshcheck", "true", { maxAge: 1000 })
.type("text/html")
.send(failureFile);
return;
}
if (!authHeader) {
reply
.code(401)
.header("WWW-Authenticate", "Basic")
.type("text/html")
.send(failureFile);
return;
}
const auth = Buffer.from(authHeader.split(" ")[1], "base64")
.toString()
.split(":");
const user = auth[0];
const pass = auth[1];
licenseCheck(req, pass).then((data) => {
if (!data) {
reply
.status(401)
.header("WWW-Authenticate", "Basic")
.type("text/html")
.send(failureFile);
return;
} else {
reply
.setCookie("authcheck", "true")
.type("text/html")
.send("<script>window.location.href = window.location.href</script>");
return;
}
});
});
done();
};
const masqr = fp(plugin, {
fastify: "4.x",
name: "masqr"
});
export default masqr;

7536
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,66 +1,21 @@
{
"name": "nebula",
"private": true,
"name": "nebula-astro",
"type": "module",
"version": "0.0.1",
"scripts": {
"dev": "concurrently \"vite\" \"tsx server.ts\"",
"build": "vite build",
"bstart": "npm run build && tsx server.ts",
"start": "tsx server.ts",
"preview": "vite preview",
"format": "prettier --write ."
"dev": "astro dev",
"start": "astro dev",
"build": "astro check && astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@fastify/compress": "^6.5.0",
"@fastify/cookie": "^9.3.1",
"@fastify/static": "^6.12.0",
"@mercuryworkshop/bare-mux": "^1.1.1",
"@mercuryworkshop/epoxy-transport": "^2.0.1",
"@mercuryworkshop/libcurl-transport": "^1.3.1",
"@nebula-services/dynamic": "0.7.2-patch.2",
"@titaniumnetwork-dev/ultraviolet": "^3.1.2",
"@tomphttp/bare-server-node": "2.0.3",
"@tsparticles/engine": "^3.4.0",
"@tsparticles/react": "^3.0.0",
"@tsparticles/slim": "^3.4.0",
"chalk": "^5.3.0",
"classnames": "^2.5.1",
"compression": "^1.7.4",
"cookie-parser": "^1.4.6",
"crypto-js": "^4.2.0",
"fastify": "^4.28.1",
"fastify-plugin": "^4.5.1",
"framer-motion": "^10.18.0",
"i18next": "^23.11.5",
"i18next-browser-languagedetector": "^7.2.1",
"localforage": "^1.10.0",
"million": "^2.6.4",
"preact": "^10.22.1",
"preact-iso": "^2.6.3",
"preact-render-to-string": "^6.5.5",
"preact-router": "^4.1.2",
"rammerhead": "https://github.com/NebulaServices/rammerhead/releases/download/rammerhead-1.2.41-nebula.8/rammerhead-1.2.41-nebula.7.tgz",
"react-helmet": "^6.1.0",
"react-i18next": "^13.5.0",
"react-icons": "^4.12.0",
"react-toastify": "^9.1.3",
"tsx": "^4.16.0",
"vite-plugin-static-copy": "^1.0.5",
"vite-plugin-vsharp": "^1.8.1",
"wisp-server-node": "^1.1.0",
"ws": "^8.17.1"
},
"devDependencies": {
"@preact/preset-vite": "^2.8.3",
"autoprefixer": "^10.4.19",
"concurrently": "^8.2.2",
"eslint": "^8.57.0",
"eslint-config-preact": "^1.4.0",
"postcss": "^8.4.39",
"prettier": "^3.3.2",
"prettier-plugin-tailwindcss": "^0.5.14",
"tailwindcss": "^3.4.4",
"typescript": "^5.5.2",
"vite": "^5.3.2"
"@astrojs/check": "^0.8.2",
"@astrojs/tailwind": "^5.1.0",
"@iconify-json/ph": "^1.1.13",
"astro": "^4.12.2",
"astro-icon": "^1.1.0",
"tailwindcss": "^3.4.6",
"typescript": "^5.5.3"
}
}

8044
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +0,0 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

View file

@ -1,110 +0,0 @@
{
"particles": {
"number": {
"value": 300,
"density": {
"enable": true,
"value_area": 800
}
},
"color": {
"value": "#ffffff"
},
"shape": {
"type": "circle",
"stroke": {
"width": 0,
"color": "#000000"
},
"polygon": {
"nb_sides": 5
},
"image": {
"src": "img/github.svg",
"width": 100,
"height": 100
}
},
"opacity": {
"value": 0.5,
"random": false,
"anim": {
"enable": false,
"speed": 1,
"opacity_min": 0.1,
"sync": false
}
},
"size": {
"value": 1.5,
"random": true,
"anim": {
"enable": false,
"speed": 40,
"size_min": 0.1,
"sync": false
}
},
"line_linked": {
"enable": false,
"distance": 150,
"color": "#ffffff",
"opacity": 0.4,
"width": 1
},
"move": {
"enable": true,
"speed": 6.3974410235905665,
"direction": "bottom-right",
"random": false,
"straight": false,
"out_mode": "out",
"bounce": false,
"attract": {
"enable": false,
"rotateX": 600,
"rotateY": 1200
}
}
},
"interactivity": {
"detect_on": "canvas",
"events": {
"onhover": {
"enable": false,
"mode": "repulse"
},
"onclick": {
"enable": false,
"mode": "push"
},
"resize": true
},
"modes": {
"grab": {
"distance": 400,
"line_linked": {
"opacity": 1
}
},
"bubble": {
"distance": 400,
"size": 40,
"duration": 2,
"opacity": 8,
"speed": 3
},
"repulse": {
"distance": 200,
"duration": 0.4
},
"push": {
"particles_nb": 4
},
"remove": {
"particles_nb": 2
}
}
},
"retina_detect": true
}

View file

@ -1,30 +0,0 @@
// See documentation for more information
self.__dynamic$config = {
prefix: '/~/dynamic/',
encoding: 'aes',
mode: 'production',
logLevel: 0,
bare: {
version: 2,
path: '/bare/',
},
tab: {
title: 'Service',
icon: null,
ua: null,
},
assets: {
prefix: '/dynamic/',
files: {
handler: 'dynamic.handler.js',
client: 'dynamic.client.js',
worker: 'dynamic.worker.js',
config: 'dynamic.config.js',
inject: null,
}
},
block: [
]
};

9
public/favicon.svg Normal file
View file

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
<style>
path { fill: #000; }
@media (prefers-color-scheme: dark) {
path { fill: #FFF; }
}
</style>
</svg>

After

Width:  |  Height:  |  Size: 749 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

View file

@ -1,110 +0,0 @@
{
"particles": {
"number": {
"value": 80,
"density": {
"enable": true,
"value_area": 800
}
},
"color": {
"value": "#ffffff"
},
"shape": {
"type": "circle",
"stroke": {
"width": 0,
"color": "#000000"
},
"polygon": {
"nb_sides": 5
},
"image": {
"src": "img/github.svg",
"width": 100,
"height": 100
}
},
"opacity": {
"value": 0.5,
"random": false,
"anim": {
"enable": false,
"speed": 1,
"opacity_min": 0.1,
"sync": false
}
},
"size": {
"value": 3,
"random": true,
"anim": {
"enable": false,
"speed": 40,
"size_min": 0.1,
"sync": false
}
},
"line_linked": {
"enable": true,
"distance": 150,
"color": "#ffffff",
"opacity": 0.4,
"width": 1
},
"move": {
"enable": true,
"speed": 6,
"direction": "none",
"random": false,
"straight": false,
"out_mode": "out",
"bounce": false,
"attract": {
"enable": false,
"rotateX": 600,
"rotateY": 1200
}
}
},
"interactivity": {
"detect_on": "canvas",
"events": {
"onhover": {
"enable": true,
"mode": "repulse"
},
"onclick": {
"enable": true,
"mode": "push"
},
"resize": true
},
"modes": {
"grab": {
"distance": 400,
"line_linked": {
"opacity": 1
}
},
"bubble": {
"distance": 400,
"size": 40,
"duration": 2,
"opacity": 8,
"speed": 3
},
"repulse": {
"distance": 200,
"duration": 0.4
},
"push": {
"particles_nb": 4
},
"remove": {
"particles_nb": 2
}
}
},
"retina_detect": true
}

View file

@ -1,5 +0,0 @@
User-agent: *
Disallow: /settings
Disallow: /~/
Disallow: /go/
Disallow: /games

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View file

@ -1,62 +0,0 @@
importScripts("/epoxy/index.js");
importScripts("/libcurl/index.js");
importScripts("/transports/bareTransport.js");
importScripts("/uv/uv.bundle.js");
importScripts("/uv/uv.config.js");
importScripts(__uv$config.sw || "/uv/uv.sw.js");
importScripts("/dynamic/dynamic.config.js");
importScripts("/dynamic/dynamic.worker.js");
//import our IDB lib
importScripts("/localforage/localforage.min.js");
localforage.config({
driver: localforage.INDEXEDDB,
name: "Nebula",
version: 1.0,
storeName: "nebula_config",
description: "Nebula Config for things reliant on IndexedDB"
});
const uv = new UVServiceWorker();
const dynPromise = new Promise(async (resolve) => {
try {
const bare =
(await localforage.getItem("bare")) || location.origin + "/bare/";
self.__dynamic$config.bare.path = bare;
self.dynamic = new Dynamic(self.__dynamic$config);
} catch (error) {
console.log(error);
}
resolve();
});
self.addEventListener("fetch", (event) => {
if (
event.request.url.startsWith(location.origin + self.__dynamic$config.prefix)
) {
event.respondWith(
(async function () {
try {
await dynPromise;
} catch (error) {}
if (await self.dynamic.route(event)) {
return await self.dynamic.fetch(event);
}
await fetch(event.request);
})()
);
} else if (
event.request.url.startsWith(location.origin + __uv$config.prefix)
) {
event.respondWith(
(async function () {
return await uv.fetch(event);
})()
);
} else {
event.respondWith(
(async function () {
return await fetch(event.request);
})()
);
}
});

View file

@ -1,612 +0,0 @@
//Built from: https://github.com/motortruck1221/bare-as-module3 (commit: 36759f801e0009027878edecff156408b06404c6)
(function (global, factory) {
typeof exports === "object" && typeof module !== "undefined"
? factory(exports)
: typeof define === "function" && define.amd
? define(["exports"], factory)
: ((global =
typeof globalThis !== "undefined" ? globalThis : global || self),
factory((global.BareMod = {})));
})(this, function (exports) {
"use strict";
// The user likely has overwritten all networking functions after importing bare-client
// It is our responsibility to make sure components of Bare-Client are using native networking functions
// These exports are provided to plugins by @rollup/plugin-inject
const fetch = globalThis.fetch;
const WebSocket = globalThis.WebSocket;
const WebSocketFields = {
prototype: {
send: WebSocket.prototype.send
},
CLOSED: WebSocket.CLOSED,
CLOSING: WebSocket.CLOSING,
CONNECTING: WebSocket.CONNECTING,
OPEN: WebSocket.OPEN
};
class BareError extends Error {
status;
body;
constructor(status, body) {
super(body.message || body.code);
this.status = status;
this.body = body;
}
}
class Client {
base;
/**
*
* @param version Version provided by extension
* @param server Bare Server URL provided by BareClient
*/
constructor(version, server) {
this.base = new URL(`./v${version}/`, server);
}
}
/*
* JavaScript MD5
* Adopted from https://github.com/blueimp/JavaScript-MD5
*
* Copyright 2011, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* https://opensource.org/licenses/MIT
*
* Based on
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
* Digest Algorithm, as defined in RFC 1321.
* Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
* Distributed under the BSD License
* See http://pajhome.org.uk/crypt/md5 for more info.
*/
/**
* Add integers, wrapping at 2^32.
* This uses 16-bit operations internally to work around bugs in interpreters.
*
* @param x First integer
* @param y Second integer
* @returns Sum
*/
function safeAdd(x, y) {
const lsw = (x & 0xffff) + (y & 0xffff);
const msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xffff);
}
/**
* Bitwise rotate a 32-bit number to the left.
*
* @param num 32-bit number
* @param cnt Rotation count
* @returns Rotated number
*/
function bitRotateLeft(num, cnt) {
return (num << cnt) | (num >>> (32 - cnt));
}
/**
* Basic operation the algorithm uses.
*
* @param q q
* @param a a
* @param b b
* @param x x
* @param s s
* @param t t
* @returns Result
*/
function md5cmn(q, a, b, x, s, t) {
return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b);
}
/**
* Basic operation the algorithm uses.
*
* @param a a
* @param b b
* @param c c
* @param d d
* @param x x
* @param s s
* @param t t
* @returns Result
*/
function md5ff(a, b, c, d, x, s, t) {
return md5cmn((b & c) | (~b & d), a, b, x, s, t);
}
/**
* Basic operation the algorithm uses.
*
* @param a a
* @param b b
* @param c c
* @param d d
* @param x x
* @param s s
* @param t t
* @returns Result
*/
function md5gg(a, b, c, d, x, s, t) {
return md5cmn((b & d) | (c & ~d), a, b, x, s, t);
}
/**
* Basic operation the algorithm uses.
*
* @param a a
* @param b b
* @param c c
* @param d d
* @param x x
* @param s s
* @param t t
* @returns Result
*/
function md5hh(a, b, c, d, x, s, t) {
return md5cmn(b ^ c ^ d, a, b, x, s, t);
}
/**
* Basic operation the algorithm uses.
*
* @param a a
* @param b b
* @param c c
* @param d d
* @param x x
* @param s s
* @param t t
* @returns Result
*/
function md5ii(a, b, c, d, x, s, t) {
return md5cmn(c ^ (b | ~d), a, b, x, s, t);
}
/**
* Calculate the MD5 of an array of little-endian words, and a bit length.
*
* @param x Array of little-endian words
* @param len Bit length
* @returns MD5 Array
*/
function binlMD5(x, len) {
/* append padding */
x[len >> 5] |= 0x80 << len % 32;
x[(((len + 64) >>> 9) << 4) + 14] = len;
let a = 1732584193;
let b = -271733879;
let c = -1732584194;
let d = 271733878;
for (let i = 0; i < x.length; i += 16) {
const olda = a;
const oldb = b;
const oldc = c;
const oldd = d;
a = md5ff(a, b, c, d, x[i], 7, -680876936);
d = md5ff(d, a, b, c, x[i + 1], 12, -389564586);
c = md5ff(c, d, a, b, x[i + 2], 17, 606105819);
b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330);
a = md5ff(a, b, c, d, x[i + 4], 7, -176418897);
d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426);
c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341);
b = md5ff(b, c, d, a, x[i + 7], 22, -45705983);
a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416);
d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417);
c = md5ff(c, d, a, b, x[i + 10], 17, -42063);
b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162);
a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682);
d = md5ff(d, a, b, c, x[i + 13], 12, -40341101);
c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290);
b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329);
a = md5gg(a, b, c, d, x[i + 1], 5, -165796510);
d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632);
c = md5gg(c, d, a, b, x[i + 11], 14, 643717713);
b = md5gg(b, c, d, a, x[i], 20, -373897302);
a = md5gg(a, b, c, d, x[i + 5], 5, -701558691);
d = md5gg(d, a, b, c, x[i + 10], 9, 38016083);
c = md5gg(c, d, a, b, x[i + 15], 14, -660478335);
b = md5gg(b, c, d, a, x[i + 4], 20, -405537848);
a = md5gg(a, b, c, d, x[i + 9], 5, 568446438);
d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690);
c = md5gg(c, d, a, b, x[i + 3], 14, -187363961);
b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501);
a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467);
d = md5gg(d, a, b, c, x[i + 2], 9, -51403784);
c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473);
b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734);
a = md5hh(a, b, c, d, x[i + 5], 4, -378558);
d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463);
c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562);
b = md5hh(b, c, d, a, x[i + 14], 23, -35309556);
a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060);
d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353);
c = md5hh(c, d, a, b, x[i + 7], 16, -155497632);
b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640);
a = md5hh(a, b, c, d, x[i + 13], 4, 681279174);
d = md5hh(d, a, b, c, x[i], 11, -358537222);
c = md5hh(c, d, a, b, x[i + 3], 16, -722521979);
b = md5hh(b, c, d, a, x[i + 6], 23, 76029189);
a = md5hh(a, b, c, d, x[i + 9], 4, -640364487);
d = md5hh(d, a, b, c, x[i + 12], 11, -421815835);
c = md5hh(c, d, a, b, x[i + 15], 16, 530742520);
b = md5hh(b, c, d, a, x[i + 2], 23, -995338651);
a = md5ii(a, b, c, d, x[i], 6, -198630844);
d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415);
c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905);
b = md5ii(b, c, d, a, x[i + 5], 21, -57434055);
a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571);
d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606);
c = md5ii(c, d, a, b, x[i + 10], 15, -1051523);
b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799);
a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359);
d = md5ii(d, a, b, c, x[i + 15], 10, -30611744);
c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380);
b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649);
a = md5ii(a, b, c, d, x[i + 4], 6, -145523070);
d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379);
c = md5ii(c, d, a, b, x[i + 2], 15, 718787259);
b = md5ii(b, c, d, a, x[i + 9], 21, -343485551);
a = safeAdd(a, olda);
b = safeAdd(b, oldb);
c = safeAdd(c, oldc);
d = safeAdd(d, oldd);
}
return [a, b, c, d];
}
/**
* Convert an array of little-endian words to a string
*
* @param input MD5 Array
* @returns MD5 string
*/
function binl2rstr(input) {
let output = "";
const length32 = input.length * 32;
for (let i = 0; i < length32; i += 8) {
output += String.fromCharCode((input[i >> 5] >>> i % 32) & 0xff);
}
return output;
}
/**
* Convert a raw string to an array of little-endian words
* Characters >255 have their high-byte silently ignored.
*
* @param input Raw input string
* @returns Array of little-endian words
*/
function rstr2binl(input) {
const output = [];
const outputLen = input.length >> 2;
for (let i = 0; i < outputLen; i += 1) {
output[i] = 0;
}
const length8 = input.length * 8;
for (let i = 0; i < length8; i += 8) {
output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << i % 32;
}
return output;
}
/**
* Calculate the MD5 of a raw string
*
* @param s Input string
* @returns Raw MD5 string
*/
function rstrMD5(s) {
return binl2rstr(binlMD5(rstr2binl(s), s.length * 8));
}
/**
* Calculates the HMAC-MD5 of a key and some data (raw strings)
*
* @param key HMAC key
* @param data Raw input string
* @returns Raw MD5 string
*/
function rstrHMACMD5(key, data) {
let bkey = rstr2binl(key);
const ipad = [];
const opad = [];
if (bkey.length > 16) {
bkey = binlMD5(bkey, key.length * 8);
}
for (let i = 0; i < 16; i += 1) {
ipad[i] = bkey[i] ^ 0x36363636;
opad[i] = bkey[i] ^ 0x5c5c5c5c;
}
const hash = binlMD5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
return binl2rstr(binlMD5(opad.concat(hash), 512 + 128));
}
/**
* Convert a raw string to a hex string
*
* @param input Raw input string
* @returns Hex encoded string
*/
function rstr2hex(input) {
const hexTab = "0123456789abcdef";
let output = "";
for (let i = 0; i < input.length; i += 1) {
const x = input.charCodeAt(i);
output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f);
}
return output;
}
/**
* Encode a string as UTF-8
*
* @param input Input string
* @returns UTF8 string
*/
function str2rstrUTF8(input) {
return unescape(encodeURIComponent(input));
}
/**
* Encodes input string as raw MD5 string
*
* @param s Input string
* @returns Raw MD5 string
*/
function rawMD5(s) {
return rstrMD5(str2rstrUTF8(s));
}
/**
* Encodes input string as Hex encoded string
*
* @param s Input string
* @returns Hex encoded string
*/
function hexMD5(s) {
return rstr2hex(rawMD5(s));
}
/**
* Calculates the raw HMAC-MD5 for the given key and data
*
* @param k HMAC key
* @param d Input string
* @returns Raw MD5 string
*/
function rawHMACMD5(k, d) {
return rstrHMACMD5(str2rstrUTF8(k), str2rstrUTF8(d));
}
/**
* Calculates the Hex encoded HMAC-MD5 for the given key and data
*
* @param k HMAC key
* @param d Input string
* @returns Raw MD5 string
*/
function hexHMACMD5(k, d) {
return rstr2hex(rawHMACMD5(k, d));
}
/**
* Calculates MD5 value for a given string.
* If a key is provided, calculates the HMAC-MD5 value.
* Returns a Hex encoded string unless the raw argument is given.
*
* @param string Input string
* @param key HMAC key
* @param raw Raw output switch
* @returns MD5 output
*/
function md5(string, key, raw) {
if (!key) {
if (!raw) {
return hexMD5(string);
}
return rawMD5(string);
}
if (!raw) {
return hexHMACMD5(key, string);
}
return rawHMACMD5(key, string);
}
const MAX_HEADER_VALUE = 3072;
/**
*
* Splits headers according to spec
* @param headers
* @returns Split headers
*/
function splitHeaders(headers) {
const output = new Headers(headers);
if (headers.has("x-bare-headers")) {
const value = headers.get("x-bare-headers");
if (value.length > MAX_HEADER_VALUE) {
output.delete("x-bare-headers");
let split = 0;
for (let i = 0; i < value.length; i += MAX_HEADER_VALUE) {
const part = value.slice(i, i + MAX_HEADER_VALUE);
const id = split++;
output.set(`x-bare-headers-${id}`, `;${part}`);
}
}
}
return output;
}
/**
* Joins headers according to spec
* @param headers
* @returns Joined headers
*/
function joinHeaders(headers) {
const output = new Headers(headers);
const prefix = "x-bare-headers";
if (headers.has(`${prefix}-0`)) {
const join = [];
for (const [header, value] of headers) {
if (!header.startsWith(prefix)) {
continue;
}
if (!value.startsWith(";")) {
throw new BareError(400, {
code: "INVALID_BARE_HEADER",
id: `request.headers.${header}`,
message: `Value didn't begin with semi-colon.`
});
}
const id = parseInt(header.slice(prefix.length + 1));
join[id] = value.slice(1);
output.delete(header);
}
output.set(prefix, join.join(""));
}
return output;
}
class ClientV3 extends Client {
ws;
http;
meta() {
return {};
}
constructor(server) {
super(3, server);
this.ws = new URL(this.base);
this.http = new URL(this.base);
if (this.ws.protocol === "https:") {
this.ws.protocol = "wss:";
} else {
this.ws.protocol = "ws:";
}
}
ready = true;
async init() {
this.ready = true;
}
connect(
url,
origin,
protocols,
requestHeaders,
onopen,
onmessage,
onclose,
onerror
) {
const ws = new WebSocket(this.ws);
const cleanup = () => {
ws.removeEventListener("close", closeListener);
ws.removeEventListener("message", messageListener);
};
const closeListener = () => {
cleanup();
};
const messageListener = (event) => {
cleanup();
// ws.binaryType is irrelevant when sending text
if (typeof event.data !== "string")
throw new TypeError(
"the first websocket message was not a text frame"
);
const message = JSON.parse(event.data);
// finally
if (message.type !== "open")
throw new TypeError("message was not of open type");
// onMeta({
// protocol: message.protocol,
// setCookies: message.setCookies,
// });
onopen(message.protocol);
// TODO
ws.addEventListener("message", (ev) => {
onmessage(ev.data);
});
};
ws.addEventListener("close", closeListener);
ws.addEventListener("message", messageListener);
// CONNECTED TO THE BARE SERVER, NOT THE REMOTE
ws.addEventListener(
"open",
(event) => {
// getRequestHeaders().then((headers:any) =>
WebSocketFields.prototype.send.call(
ws,
JSON.stringify({
type: "connect",
remote: url.toString(),
protocols,
headers: requestHeaders,
forwardHeaders: []
})
);
// );
},
// only block the open event once
{ once: true }
);
return ws.send.bind(ws);
}
async request(remote, method, body, headers, signal) {
const options = {
credentials: "omit",
method: method,
signal
};
if (body !== undefined) {
options.body = body;
}
options.headers = this.createBareHeaders(remote, headers);
const response = await fetch(
this.http + "?cache=" + md5(remote.toString()),
options
);
const readResponse = await this.readBareResponse(response);
// const result: Response & Partial<BareResponse> = new Response(
// statusEmpty.includes(readResponse.status!) ? undefined : response.body,
// {
// status: readResponse.status,
// statusText: readResponse.statusText ?? undefined,
// headers: new Headers(readResponse.headers as HeadersInit),
// }
// );
//
// result.rawHeaders = readResponse.headers;
// result.rawResponse = response;
return {
body: response.body,
headers: readResponse.headers,
status: readResponse.status,
statusText: readResponse.statusText
};
}
async readBareResponse(response) {
if (!response.ok) {
throw new BareError(response.status, await response.json());
}
const responseHeaders = joinHeaders(response.headers);
const result = {};
const xBareStatus = responseHeaders.get("x-bare-status");
if (xBareStatus !== null) result.status = parseInt(xBareStatus);
const xBareStatusText = responseHeaders.get("x-bare-status-text");
if (xBareStatusText !== null) result.statusText = xBareStatusText;
const xBareHeaders = responseHeaders.get("x-bare-headers");
if (xBareHeaders !== null) result.headers = JSON.parse(xBareHeaders);
return result;
}
createBareHeaders(
remote,
bareHeaders,
forwardHeaders = [],
passHeaders = [],
passStatus = []
) {
const headers = new Headers();
headers.set("x-bare-url", remote.toString());
headers.set("x-bare-headers", JSON.stringify(bareHeaders));
for (const header of forwardHeaders) {
headers.append("x-bare-forward-headers", header);
}
for (const header of passHeaders) {
headers.append("x-bare-pass-headers", header);
}
for (const status of passStatus) {
headers.append("x-bare-pass-status", status.toString());
}
splitHeaders(headers);
return headers;
}
}
exports.BareClient = ClientV3;
});
//# sourceMappingURL=bare.cjs.map

View file

@ -1,36 +0,0 @@
self.__uv$config = {
prefix: "/~/uv/",
bare: "/bare/",
encodeUrl: function encode(str) {
if (!str) return str;
return encodeURIComponent(
str
.toString()
.split('')
.map((char, ind) =>
ind % 2 ? String.fromCharCode(char.charCodeAt() ^ 3) : char
)
.join('')
);
},
decodeUrl: function decode(str) {
if (!str) return str;
let [input, ...search] = str.split('?');
return (
decodeURIComponent(input)
.split('')
.map((char, ind) =>
ind % 2 ? String.fromCharCode(char.charCodeAt(0) ^ 3) : char
)
.join('') + (search.length ? '?' + search.join('?') : '')
);
},
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",
proxyIp: "",
proxyPort: ""
};

113
server.ts
View file

@ -1,113 +0,0 @@
import fastify from "fastify";
import fastifyStatic from "@fastify/static";
import { fileURLToPath } from "url";
import path from "path";
import cookieParser from "@fastify/cookie";
import { createServer } from "http";
import { createBareServer } from "@tomphttp/bare-server-node";
import createRammerhead from "rammerhead/src/server/index.js";
import wisp from "wisp-server-node";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const bare = createBareServer("/bare/");
const rh = createRammerhead();
import chalk from "chalk";
import masqr from "./masqr.js";
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);
}
const serverFactory = (handler, opts) => {
return createServer()
.on("request", (req, res) => {
if (bare.shouldRoute(req)) {
bare.routeRequest(req, res);
} else if (shouldRouteRh(req)) {
routeRhRequest(req, res);
} else {
handler(req, res);
}
})
.on("upgrade", (req, socket, head) => {
if (bare.shouldRoute(req)) {
bare.routeUpgrade(req, socket, head);
} else if (shouldRouteRh(req)) {
routeRhUpgrade(req, socket, head);
} else if (req.url.endsWith("/wisp/")) {
wisp.routeRequest(req, socket, head);
}
});
};
const app = fastify({ logger: false, serverFactory });
app.register(cookieParser);
await app.register(import("@fastify/compress"));
//Uncomment the following line to enable masqr
//app.register(masqr);
app.register(fastifyStatic, {
root: path.join(__dirname, "dist"),
prefix: "/",
serve: true,
wildcard: false
});
app.get("/search=:query", async (req, res) => {
const { query } = req.params as { query: string }; // Define the type for req.params
const response = await fetch(
`http://api.duckduckgo.com/ac?q=${query}&format=json`
).then((apiRes) => apiRes.json());
res.send(response);
});
app.setNotFoundHandler((req, res) => {
res.sendFile("index.html"); // SPA catch-all
});
console.log(
chalk.green(`Server listening on ${chalk.bold("http://localhost:8080")}`)
);
console.log(
chalk.magenta(`Server also listening on ${chalk.bold("http://0.0.0.0:8080")}`)
);
app.listen({
port: 8080,
host: "0.0.0.0"
});

View file

@ -1,25 +0,0 @@
import { LoadSuspense } from "./LoadSuspense";
export function AboutBlank(props: { url: string }) {
var newWindow = window.open("about:blank");
var iframe = document.createElement("iframe");
iframe.src = window.location.origin + props.url;
iframe.style.width = "100%";
iframe.style.height = "100%";
iframe.style.border = "none";
iframe.style.overflow = "hidden";
iframe.style.margin = "0";
iframe.style.padding = "0";
iframe.style.position = "fixed";
iframe.style.top = "0";
iframe.style.bottom = "0";
iframe.style.left = "0";
iframe.style.right = "0";
newWindow.document.body.appendChild(iframe);
window.location.replace("https://google.com");
return (
<div>
<LoadSuspense />
</div>
);
}

View file

@ -1,26 +0,0 @@
import "./Suspense.css";
export function LoadSuspense() {
return (
<div className="suspenseContainer">
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.2"
viewBox="0 0 400 400"
width="400"
height="400"
fill="#5e17eb"
className="animate-pulse-brighter"
>
<g id="svgg">
<path
id="path0"
fill-rule="evenodd"
class="s0"
d="m213.6 84c1 0.3 3.4 0.7 5.1 1 1.8 0.2 4.1 0.7 5.2 1 13.2 4.1 20.3 6.8 24.5 9.1 0.6 0.3 2.3 1.2 3.8 2 2.8 1.4 13.1 8 14.4 9.2 0.5 0.3 2.3 1.9 4.2 3.5 6.7 5.5 15.5 14.9 19.2 20.4 1 1.4 2 2.7 2.2 2.8 0.3 0.1 0.5 0.5 0.5 0.8 0 0.3 1.2 2.2 2.5 4.3 2.3 3.4 7.8 14.3 9.8 19.3 0.8 2.1 0.9 2.2 10 4.9 5.6 1.6 11.1 3.4 11.7 3.8 0.3 0.2 2.4 1.1 4.7 1.9 11.1 4.1 23 12.5 27.3 19.4 5.5 8.7 3.6 20.5-4.5 28.5-3.1 3-7.5 6.4-8.4 6.4-0.3 0-0.7 0.2-0.8 0.5-1.1 2.3-23.3 11.2-35.9 14.3-3.2 0.9-3.5 1.2-5.7 6.8-7.5 19-25.5 40.6-42.3 51.1-1.6 1-3.1 2-3.3 2.2-0.1 0.2-0.9 0.7-1.7 1.2-0.8 0.4-2.2 1.2-2.9 1.6-0.8 0.4-1.6 1-1.8 1.1-0.5 0.5-4.1 2.2-8.1 3.9-1.8 0.8-3.8 1.6-4.5 2-0.7 0.3-3.1 1.1-5.2 1.8-8.3 2.6-9.8 3-21 4.9-6.4 1.1-25.3 1.3-30.5 0.3-1.9-0.3-5.8-1.1-8.6-1.6-6.8-1.3-12.7-3-20-5.7-3.3-1.2-18-8.8-19.7-10.1-0.9-0.7-4.1-3.1-7.1-5.2-5.7-4.1-17.9-15.9-20.7-20-0.9-1.2-2-2.7-2.5-3.2-3.2-3.3-13.7-21.7-13.7-24.1 0-0.6-0.2-1.2-0.6-1.4-0.3-0.2-0.8-1.2-1-2.3-0.4-1.9-1.7-2.7-6.5-3.8-23.2-5.6-43.1-17.2-48.6-28.5-7.1-14.4 4.5-31.3 27.3-39.7 1.8-0.7 4.1-1.6 5.2-2 3.7-1.5 8-2.9 19.5-6.2 1.6-0.5 2.8-1.2 2.8-1.7 0-2.4 9.8-21.6 13.1-25.7 0.2-0.4 1.4-1.8 2.5-3.2 13.8-18.1 30.2-30.6 50.6-38.8 4.3-1.7 6-2.3 14.3-4.5 5.5-1.6 11.2-2.4 18.4-2.9 7.9-0.5 24-0.2 26.8 0.6zm-29.7 17.3c-0.2 0.1-7.3 1.7-12.9 2.7-1.7 0.4-4.3 1.2-5.8 1.9-1.5 0.6-3.9 1.5-5.5 2-1.5 0.4-3.3 1.3-4 2-0.7 0.7-1.7 1.2-2.3 1.2-1.2 0-9.5 4.5-9.8 5.3-0.2 0.3-0.5 0.6-0.9 0.6-1.9 0-19.6 16-23.8 21.6-9.3 12.2-16.1 27.4-19.2 42.7-2 10.1-1.1 37.5 1.4 41.4 0 0.1 3.7 0.9 8.1 1.8 9.5 1.9 12.8 2.4 34.6 4.9 38.5 4.5 107.9 2.2 138.3-4.5 1.4-0.3 4.1-0.9 6.1-1.3 4.2-0.8 3.4 0.2 4.9-7.1 1.6-8.3 1.7-27 0.1-34.6-1.5-7.1-3.2-13.4-3.7-14.2-0.3-0.4-0.7-1.5-0.9-2.6-2.8-12.1-19.2-34.1-33-44.3-2.9-2.1-5.5-4-5.8-4.3-2.8-2.2-4.9-3.1-7.2-3.1-2.1 0-2.6-0.2-2.7-1.3-0.2-1.5-5.7-4.5-6.3-3.5-0.7 1.1-2.4 0.6-2.7-0.7-0.4-1.3-1.2-1.6-5.8-2.1-1.6-0.2-4-0.9-5.5-1.6-3.9-1.8-5.3-2.2-10.2-2.6-4.6-0.4-25.2-0.7-25.5-0.3zm74.3 42.2c7.4 9.8 4.8 23.5-4.6 24.9-6.9 1-20.9-5.8-21-10.2 0-0.2-0.3-0.8-0.8-1.3-6.4-6.8-5-20.8 2.4-24.1 6.7-2.9 17.2 1.8 24 10.7zm-176.4 36.4c-0.1-0.1-4.6 1.1-5.9 1.6-0.7 0.3-3 1.2-5.1 2-9.9 3.8-15.1 6.8-19.6 11.5-3.4 3.5-3.3 4.5 0.5 8.7 1 1 11.3 7.6 12 7.6 0.2 0 1.7 0.6 3.4 1.3 1.6 0.8 3.6 1.6 4.3 1.9 1.8 0.8 9.3 3.3 9.9 3.3 0.3 0 0.3-2 0-4.4-0.6-5.6-0.6-24.5 0.1-29.6 0.3-2.1 0.5-3.9 0.4-3.9zm229.3-0.3c-0.2 0 0 1 0.2 2.1 0.6 2.8 0.6 31.3 0 34-0.5 2.2-0.4 2.2 1.3 1.8 3.1-0.7 12.9-4.5 18.3-7 8.5-4 14.3-10.1 12.6-13.3-1.1-2.1-6.7-7.2-7.9-7.2-0.4 0-0.9-0.2-1-0.5-0.4-1.1-11.8-6.1-19.2-8.5-2.3-0.7-4.2-1.4-4.3-1.4zm-199.4 63.4l-3.1-0.4 1.8 3.2c0.9 1.8 1.9 3.4 2.2 3.5 0.3 0.1 0.5 0.6 0.5 1.1 0 0.4 0.6 1.5 1.3 2.3 0.7 0.9 1.5 1.9 1.8 2.2 0.3 0.4 0.8 1.2 1.1 1.7 6.2 10.7 35.6 33.5 43.3 33.5 0.2 0 1.3 0.4 2.5 0.9 2.5 1.2 10.6 3.4 15.3 4.2 9.5 1.8 11.6 2.1 17.4 2.1 6.6 0 16.4-1.3 22.9-3 2.2-0.5 5.2-1.3 6.8-1.7 1.6-0.3 3.2-0.9 3.5-1.2 0.4-0.3 1.1-0.6 1.6-0.6 2.3 0 22-10.6 24-12.9 0.2-0.2 2.2-1.9 4.5-3.7 5.7-4.5 11.8-11 17.1-18.4 1.6-2.3 3.2-4.5 3.6-4.9 0.4-0.4 0.7-1 0.7-1.2 0-0.2 0.8-1.9 1.9-3.6 1.1-1.7 1.9-3.2 1.9-3.4 0-0.2-3.8 0.4-11 1.6-31.7 5.4-85.1 6.7-126.9 3.1-9.6-0.8-23.1-2.3-27.8-3.2-2.2-0.4-5.3-0.9-6.9-1.2z"
/>
</g>
</svg>
</div>
);
}

View file

@ -1,23 +0,0 @@
.suspenseContainer {
display: flex;
height: 100vh;
width: 100vw;
align-items: center;
justify-content: center;
background-color: #191724;
}
@keyframes pulse-brighter {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
.animate-pulse-brighter {
animation: pulse-brighter 2s infinite;
height: 11rem;
}

View file

@ -1,30 +0,0 @@
import { useRef, useEffect } from "preact/hooks";
export default function Video({ src, isMuted }) {
const refVideo = useRef(null);
useEffect(() => {
if (!refVideo.current) {
return;
}
if (isMuted) {
//open bug since 2017 that you cannot set muted in video element https://github.com/facebook/react/issues/10389
refVideo.current.defaultMuted = true;
refVideo.current.muted = true;
}
refVideo.current.srcObject = src;
}, [src]);
return (
<video
ref={refVideo}
autoPlay
playsInline //FIX iOS black screen
className="relative z-10 h-screen w-full"
>
<source src={src} type="video/mp4" />
</video>
);
}

View file

@ -1,745 +0,0 @@
var Nr = 14,
Nk = 8,
Decrypt = false,
enc_utf8 = function (s) {
try {
return unescape(encodeURIComponent(s));
} catch (e) {
throw "Error on UTF-8 encode";
}
},
dec_utf8 = function (s) {
try {
return decodeURIComponent(escape(s));
} catch (e) {
throw "Bad Key";
}
},
padBlock = function (byteArr) {
var array = [],
cpad,
i;
if (byteArr.length < 16) {
cpad = 16 - byteArr.length;
array = [
cpad,
cpad,
cpad,
cpad,
cpad,
cpad,
cpad,
cpad,
cpad,
cpad,
cpad,
cpad,
cpad,
cpad,
cpad,
cpad
];
}
for (i = 0; i < byteArr.length; i++) {
array[i] = byteArr[i];
}
return array;
},
block2s = function (block, lastBlock) {
var string = "",
padding,
i;
if (lastBlock) {
padding = block[15];
if (padding > 16) {
throw "Decryption error: Maybe bad key";
}
if (padding === 16) {
return "";
}
for (i = 0; i < 16 - padding; i++) {
string += String.fromCharCode(block[i]);
}
} else {
for (i = 0; i < 16; i++) {
string += String.fromCharCode(block[i]);
}
}
return string;
},
a2h = function (numArr) {
var string = "",
i;
for (i = 0; i < numArr.length; i++) {
string += (numArr[i] < 16 ? "0" : "") + numArr[i].toString(16);
}
return string;
},
h2a = function (s) {
var ret = [];
s.replace(/(..)/g, function (s) {
ret.push(parseInt(s, 16));
});
return ret;
},
s2a = function (string, binary) {
var array = [],
i;
if (!binary) {
string = enc_utf8(string);
}
for (i = 0; i < string.length; i++) {
array[i] = string.charCodeAt(i);
}
return array;
},
size = function (newsize) {
switch (newsize) {
case 128:
Nr = 10;
Nk = 4;
break;
case 192:
Nr = 12;
Nk = 6;
break;
case 256:
Nr = 14;
Nk = 8;
break;
default:
throw "Invalid Key Size Specified:" + newsize;
}
},
randArr = function (num) {
var result = [],
i;
for (i = 0; i < num; i++) {
result = result.concat(Math.floor(Math.random() * 256));
}
return result;
},
openSSLKey = function (passwordArr, saltArr) {
var rounds = Nr >= 12 ? 3 : 2,
key = [],
iv = [],
md5_hash = [],
result = [],
data00 = passwordArr.concat(saltArr),
i;
md5_hash[0] = MD5(data00);
result = md5_hash[0];
for (i = 1; i < rounds; i++) {
md5_hash[i] = MD5(md5_hash[i - 1].concat(data00));
result = result.concat(md5_hash[i]);
}
key = result.slice(0, 4 * Nk);
iv = result.slice(4 * Nk, 4 * Nk + 16);
return {
key: key,
iv: iv
};
},
rawEncrypt = function (plaintext, key, iv) {
key = expandKey(key);
var numBlocks = Math.ceil(plaintext.length / 16),
blocks = [],
i,
cipherBlocks = [];
for (i = 0; i < numBlocks; i++) {
blocks[i] = padBlock(plaintext.slice(i * 16, i * 16 + 16));
}
if (plaintext.length % 16 === 0) {
blocks.push([
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16
]);
numBlocks++;
}
for (i = 0; i < blocks.length; i++) {
blocks[i] =
i === 0
? xorBlocks(blocks[i], iv)
: xorBlocks(blocks[i], cipherBlocks[i - 1]);
cipherBlocks[i] = encryptBlock(blocks[i], key);
}
return cipherBlocks;
},
rawDecrypt = function (cryptArr, key, iv, binary) {
key = expandKey(key);
var numBlocks = cryptArr.length / 16,
cipherBlocks = [],
i,
plainBlocks = [],
string = "";
for (i = 0; i < numBlocks; i++) {
cipherBlocks.push(cryptArr.slice(i * 16, (i + 1) * 16));
}
for (i = cipherBlocks.length - 1; i >= 0; i--) {
plainBlocks[i] = decryptBlock(cipherBlocks[i], key);
plainBlocks[i] =
i === 0
? xorBlocks(plainBlocks[i], iv)
: xorBlocks(plainBlocks[i], cipherBlocks[i - 1]);
}
for (i = 0; i < numBlocks - 1; i++) {
string += block2s(plainBlocks[i], false);
}
string += block2s(plainBlocks[i], true);
return binary ? string : dec_utf8(string);
},
encryptBlock = function (block, words) {
Decrypt = false;
var state = addRoundKey(block, words, 0),
round;
for (round = 1; round < Nr + 1; round++) {
state = subBytes(state);
state = shiftRows(state);
if (round < Nr) {
state = mixColumns(state);
}
state = addRoundKey(state, words, round);
}
return state;
},
decryptBlock = function (block, words) {
Decrypt = true;
var state = addRoundKey(block, words, Nr),
round;
for (round = Nr - 1; round > -1; round--) {
state = shiftRows(state);
state = subBytes(state);
state = addRoundKey(state, words, round);
if (round > 0) {
state = mixColumns(state);
}
}
return state;
},
subBytes = function (state) {
var S = Decrypt ? SBoxInv : SBox,
temp = [],
i;
for (i = 0; i < 16; i++) {
temp[i] = S[state[i]];
}
return temp;
},
shiftRows = function (state) {
var temp = [],
shiftBy = Decrypt
? [0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3]
: [0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11],
i;
for (i = 0; i < 16; i++) {
temp[i] = state[shiftBy[i]];
}
return temp;
},
mixColumns = function (state) {
var t = [],
c;
if (!Decrypt) {
for (c = 0; c < 4; c++) {
t[c * 4] =
G2X[state[c * 4]] ^
G3X[state[1 + c * 4]] ^
state[2 + c * 4] ^
state[3 + c * 4];
t[1 + c * 4] =
state[c * 4] ^
G2X[state[1 + c * 4]] ^
G3X[state[2 + c * 4]] ^
state[3 + c * 4];
t[2 + c * 4] =
state[c * 4] ^
state[1 + c * 4] ^
G2X[state[2 + c * 4]] ^
G3X[state[3 + c * 4]];
t[3 + c * 4] =
G3X[state[c * 4]] ^
state[1 + c * 4] ^
state[2 + c * 4] ^
G2X[state[3 + c * 4]];
}
} else {
for (c = 0; c < 4; c++) {
t[c * 4] =
GEX[state[c * 4]] ^
GBX[state[1 + c * 4]] ^
GDX[state[2 + c * 4]] ^
G9X[state[3 + c * 4]];
t[1 + c * 4] =
G9X[state[c * 4]] ^
GEX[state[1 + c * 4]] ^
GBX[state[2 + c * 4]] ^
GDX[state[3 + c * 4]];
t[2 + c * 4] =
GDX[state[c * 4]] ^
G9X[state[1 + c * 4]] ^
GEX[state[2 + c * 4]] ^
GBX[state[3 + c * 4]];
t[3 + c * 4] =
GBX[state[c * 4]] ^
GDX[state[1 + c * 4]] ^
G9X[state[2 + c * 4]] ^
GEX[state[3 + c * 4]];
}
}
return t;
},
addRoundKey = function (state, words, round) {
var temp = [],
i;
for (i = 0; i < 16; i++) {
temp[i] = state[i] ^ words[round][i];
}
return temp;
},
xorBlocks = function (block1, block2) {
var temp = [],
i;
for (i = 0; i < 16; i++) {
temp[i] = block1[i] ^ block2[i];
}
return temp;
},
expandKey = function (key) {
var w = [],
temp = [],
i,
r,
t,
flat = [],
j;
for (i = 0; i < Nk; i++) {
r = [key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]];
w[i] = r;
}
for (i = Nk; i < 4 * (Nr + 1); i++) {
w[i] = [];
for (t = 0; t < 4; t++) {
temp[t] = w[i - 1][t];
}
if (i % Nk === 0) {
temp = subWord(rotWord(temp));
temp[0] ^= Rcon[i / Nk - 1];
} else if (Nk > 6 && i % Nk === 4) {
temp = subWord(temp);
}
for (t = 0; t < 4; t++) {
w[i][t] = w[i - Nk][t] ^ temp[t];
}
}
for (i = 0; i < Nr + 1; i++) {
flat[i] = [];
for (j = 0; j < 4; j++) {
flat[i].push(
w[i * 4 + j][0],
w[i * 4 + j][1],
w[i * 4 + j][2],
w[i * 4 + j][3]
);
}
}
return flat;
},
subWord = function (w) {
for (var i = 0; i < 4; i++) {
w[i] = SBox[w[i]];
}
return w;
},
rotWord = function (w) {
var tmp = w[0],
i;
for (i = 0; i < 3; i++) {
w[i] = w[i + 1];
}
w[3] = tmp;
return w;
},
strhex = function (str, size) {
var i,
ret = [];
for (i = 0; i < str.length; i += size) {
ret[i / size] = parseInt(str.substr(i, size), 16);
}
return ret;
},
invertArr = function (arr) {
var i,
ret = [];
for (i = 0; i < arr.length; i++) {
ret[arr[i]] = i;
}
return ret;
},
Gxx = function (a, b) {
var i, ret;
ret = 0;
for (i = 0; i < 8; i++) {
ret = (b & 1) === 1 ? ret ^ a : ret;
a = a > 0x7f ? 0x11b ^ (a << 1) : a << 1;
b >>>= 1;
}
return ret;
},
Gx = function (x) {
var i,
r = [];
for (i = 0; i < 256; i++) {
r[i] = Gxx(x, i);
}
return r;
},
SBox = strhex(
"637c777bf26b6fc53001672bfed7ab76ca82c97dfa5947f0add4a2af9ca472c0b7fd9326363ff7cc34a5e5f171d8311504c723c31896059a071280e2eb27b27509832c1a1b6e5aa0523bd6b329e32f8453d100ed20fcb15b6acbbe394a4c58cfd0efaafb434d338545f9027f503c9fa851a3408f929d38f5bcb6da2110fff3d2cd0c13ec5f974417c4a77e3d645d197360814fdc222a908846eeb814de5e0bdbe0323a0a4906245cc2d3ac629195e479e7c8376d8dd54ea96c56f4ea657aae08ba78252e1ca6b4c6e8dd741f4bbd8b8a703eb5664803f60e613557b986c11d9ee1f8981169d98e949b1e87e9ce5528df8ca1890dbfe6426841992d0fb054bb16",
2
),
SBoxInv = invertArr(SBox),
Rcon = strhex(
"01020408102040801b366cd8ab4d9a2f5ebc63c697356ad4b37dfaefc591",
2
),
G2X = Gx(2),
G3X = Gx(3),
G9X = Gx(9),
GBX = Gx(0xb),
GDX = Gx(0xd),
GEX = Gx(0xe),
enc = function (string, pass, binary) {
var salt = randArr(8),
pbe = openSSLKey(s2a(pass, binary), salt),
key = pbe.key,
iv = pbe.iv,
cipherBlocks,
saltBlock = [[83, 97, 108, 116, 101, 100, 95, 95].concat(salt)];
string = s2a(string, binary);
cipherBlocks = rawEncrypt(string, key, iv);
cipherBlocks = saltBlock.concat(cipherBlocks);
// @ts-ignore
return Base64.encode(cipherBlocks);
},
dec = function (string, pass, binary) {
var cryptArr = Base64.decode(string),
salt = cryptArr.slice(8, 16),
pbe = openSSLKey(s2a(pass, binary), salt),
key = pbe.key,
iv = pbe.iv;
cryptArr = cryptArr.slice(16, cryptArr.length);
string = rawDecrypt(cryptArr, key, iv, binary);
return string;
},
MD5 = function (numArr) {
function rotateLeft(lValue, iShiftBits) {
return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
}
function addUnsigned(lX, lY) {
var lX4, lY4, lX8, lY8, lResult;
lX8 = lX & 0x80000000;
lY8 = lY & 0x80000000;
lX4 = lX & 0x40000000;
lY4 = lY & 0x40000000;
lResult = (lX & 0x3fffffff) + (lY & 0x3fffffff);
if (lX4 & lY4) {
return lResult ^ 0x80000000 ^ lX8 ^ lY8;
}
if (lX4 | lY4) {
if (lResult & 0x40000000) {
return lResult ^ 0xc0000000 ^ lX8 ^ lY8;
} else {
return lResult ^ 0x40000000 ^ lX8 ^ lY8;
}
} else {
return lResult ^ lX8 ^ lY8;
}
}
function f(x, y, z) {
return (x & y) | (~x & z);
}
function g(x, y, z) {
return (x & z) | (y & ~z);
}
function h(x, y, z) {
return x ^ y ^ z;
}
function funcI(x, y, z) {
return y ^ (x | ~z);
}
function ff(a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(f(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
}
function gg(a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(g(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
}
function hh(a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(h(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
}
function ii(a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(funcI(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
}
function convertToWordArray(numArr) {
var lWordCount,
lMessageLength = numArr.length,
lNumberOfWords_temp1 = lMessageLength + 8,
lNumberOfWords_temp2 =
(lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64,
lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16,
lWordArray = [],
lBytePosition = 0,
lByteCount = 0;
while (lByteCount < lMessageLength) {
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] =
lWordArray[lWordCount] | (numArr[lByteCount] << lBytePosition);
lByteCount++;
}
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
return lWordArray;
}
function wordToHex(lValue) {
var lByte,
lCount,
wordToHexArr = [];
for (lCount = 0; lCount <= 3; lCount++) {
lByte = (lValue >>> (lCount * 8)) & 255;
wordToHexArr = wordToHexArr.concat(lByte);
}
return wordToHexArr;
}
var x = [],
k,
AA,
BB,
CC,
DD,
a,
b,
c,
d,
rnd = strhex(
"67452301efcdab8998badcfe10325476d76aa478e8c7b756242070dbc1bdceeef57c0faf4787c62aa8304613fd469501698098d88b44f7afffff5bb1895cd7be6b901122fd987193a679438e49b40821f61e2562c040b340265e5a51e9b6c7aad62f105d02441453d8a1e681e7d3fbc821e1cde6c33707d6f4d50d87455a14eda9e3e905fcefa3f8676f02d98d2a4c8afffa39428771f6816d9d6122fde5380ca4beea444bdecfa9f6bb4b60bebfbc70289b7ec6eaa127fad4ef308504881d05d9d4d039e6db99e51fa27cf8c4ac5665f4292244432aff97ab9423a7fc93a039655b59c38f0ccc92ffeff47d85845dd16fa87e4ffe2ce6e0a30143144e0811a1f7537e82bd3af2352ad7d2bbeb86d391",
8
);
x = convertToWordArray(numArr);
a = rnd[0];
b = rnd[1];
c = rnd[2];
d = rnd[3];
for (k = 0; k < x.length; k += 16) {
AA = a;
BB = b;
CC = c;
DD = d;
a = ff(a, b, c, d, x[k + 0], 7, rnd[4]);
d = ff(d, a, b, c, x[k + 1], 12, rnd[5]);
c = ff(c, d, a, b, x[k + 2], 17, rnd[6]);
b = ff(b, c, d, a, x[k + 3], 22, rnd[7]);
a = ff(a, b, c, d, x[k + 4], 7, rnd[8]);
d = ff(d, a, b, c, x[k + 5], 12, rnd[9]);
c = ff(c, d, a, b, x[k + 6], 17, rnd[10]);
b = ff(b, c, d, a, x[k + 7], 22, rnd[11]);
a = ff(a, b, c, d, x[k + 8], 7, rnd[12]);
d = ff(d, a, b, c, x[k + 9], 12, rnd[13]);
c = ff(c, d, a, b, x[k + 10], 17, rnd[14]);
b = ff(b, c, d, a, x[k + 11], 22, rnd[15]);
a = ff(a, b, c, d, x[k + 12], 7, rnd[16]);
d = ff(d, a, b, c, x[k + 13], 12, rnd[17]);
c = ff(c, d, a, b, x[k + 14], 17, rnd[18]);
b = ff(b, c, d, a, x[k + 15], 22, rnd[19]);
a = gg(a, b, c, d, x[k + 1], 5, rnd[20]);
d = gg(d, a, b, c, x[k + 6], 9, rnd[21]);
c = gg(c, d, a, b, x[k + 11], 14, rnd[22]);
b = gg(b, c, d, a, x[k + 0], 20, rnd[23]);
a = gg(a, b, c, d, x[k + 5], 5, rnd[24]);
d = gg(d, a, b, c, x[k + 10], 9, rnd[25]);
c = gg(c, d, a, b, x[k + 15], 14, rnd[26]);
b = gg(b, c, d, a, x[k + 4], 20, rnd[27]);
a = gg(a, b, c, d, x[k + 9], 5, rnd[28]);
d = gg(d, a, b, c, x[k + 14], 9, rnd[29]);
c = gg(c, d, a, b, x[k + 3], 14, rnd[30]);
b = gg(b, c, d, a, x[k + 8], 20, rnd[31]);
a = gg(a, b, c, d, x[k + 13], 5, rnd[32]);
d = gg(d, a, b, c, x[k + 2], 9, rnd[33]);
c = gg(c, d, a, b, x[k + 7], 14, rnd[34]);
b = gg(b, c, d, a, x[k + 12], 20, rnd[35]);
a = hh(a, b, c, d, x[k + 5], 4, rnd[36]);
d = hh(d, a, b, c, x[k + 8], 11, rnd[37]);
c = hh(c, d, a, b, x[k + 11], 16, rnd[38]);
b = hh(b, c, d, a, x[k + 14], 23, rnd[39]);
a = hh(a, b, c, d, x[k + 1], 4, rnd[40]);
d = hh(d, a, b, c, x[k + 4], 11, rnd[41]);
c = hh(c, d, a, b, x[k + 7], 16, rnd[42]);
b = hh(b, c, d, a, x[k + 10], 23, rnd[43]);
a = hh(a, b, c, d, x[k + 13], 4, rnd[44]);
d = hh(d, a, b, c, x[k + 0], 11, rnd[45]);
c = hh(c, d, a, b, x[k + 3], 16, rnd[46]);
b = hh(b, c, d, a, x[k + 6], 23, rnd[47]);
a = hh(a, b, c, d, x[k + 9], 4, rnd[48]);
d = hh(d, a, b, c, x[k + 12], 11, rnd[49]);
c = hh(c, d, a, b, x[k + 15], 16, rnd[50]);
b = hh(b, c, d, a, x[k + 2], 23, rnd[51]);
a = ii(a, b, c, d, x[k + 0], 6, rnd[52]);
d = ii(d, a, b, c, x[k + 7], 10, rnd[53]);
c = ii(c, d, a, b, x[k + 14], 15, rnd[54]);
b = ii(b, c, d, a, x[k + 5], 21, rnd[55]);
a = ii(a, b, c, d, x[k + 12], 6, rnd[56]);
d = ii(d, a, b, c, x[k + 3], 10, rnd[57]);
c = ii(c, d, a, b, x[k + 10], 15, rnd[58]);
b = ii(b, c, d, a, x[k + 1], 21, rnd[59]);
a = ii(a, b, c, d, x[k + 8], 6, rnd[60]);
d = ii(d, a, b, c, x[k + 15], 10, rnd[61]);
c = ii(c, d, a, b, x[k + 6], 15, rnd[62]);
b = ii(b, c, d, a, x[k + 13], 21, rnd[63]);
a = ii(a, b, c, d, x[k + 4], 6, rnd[64]);
d = ii(d, a, b, c, x[k + 11], 10, rnd[65]);
c = ii(c, d, a, b, x[k + 2], 15, rnd[66]);
b = ii(b, c, d, a, x[k + 9], 21, rnd[67]);
a = addUnsigned(a, AA);
b = addUnsigned(b, BB);
c = addUnsigned(c, CC);
d = addUnsigned(d, DD);
}
return wordToHex(a).concat(wordToHex(b), wordToHex(c), wordToHex(d));
},
encString = function (plaintext, key, iv) {
var i;
plaintext = s2a(plaintext, false);
key = s2a(key, false);
for (i = key.length; i < 32; i++) {
key[i] = 0;
}
if (iv === undefined) {
} else {
iv = s2a(iv, false);
for (i = iv.length; i < 16; i++) {
iv[i] = 0;
}
}
var ct = rawEncrypt(plaintext, key, iv);
var ret = [iv];
for (i = 0; i < ct.length; i++) {
ret[ret.length] = ct[i];
}
// @ts-ignore
return Base64.encode(ret);
},
decString = function (ciphertext, key) {
var tmp = Base64.decode(ciphertext);
var iv = tmp.slice(0, 16);
var ct = tmp.slice(16, tmp.length);
var i;
key = s2a(key, false);
for (i = key.length; i < 32; i++) {
key[i] = 0;
}
var pt = rawDecrypt(ct, key, iv, false);
return pt;
},
Base64 = (function () {
var _chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
chars = _chars.split(""),
encode = function (b, withBreaks) {
var flatArr = [],
b64 = "",
i,
broken_b64,
totalChunks = Math.floor((b.length * 16) / 3);
for (i = 0; i < b.length * 16; i++) {
flatArr.push(b[Math.floor(i / 16)][i % 16]);
}
for (i = 0; i < flatArr.length; i = i + 3) {
b64 += chars[flatArr[i] >> 2];
b64 += chars[((flatArr[i] & 3) << 4) | (flatArr[i + 1] >> 4)];
if (flatArr[i + 1] !== undefined) {
b64 += chars[((flatArr[i + 1] & 15) << 2) | (flatArr[i + 2] >> 6)];
} else {
b64 += "=";
}
if (flatArr[i + 2] !== undefined) {
b64 += chars[flatArr[i + 2] & 63];
} else {
b64 += "=";
}
}
broken_b64 = b64.slice(0, 64) + "\n";
for (i = 1; i < Math.ceil(b64.length / 64); i++) {
broken_b64 +=
b64.slice(i * 64, i * 64 + 64) +
(Math.ceil(b64.length / 64) === i + 1 ? "" : "\n");
}
return broken_b64;
},
decode = function (string) {
string = string.replace(/\n/g, "");
var flatArr = [],
c = [],
b = [],
i;
for (i = 0; i < string.length; i = i + 4) {
c[0] = _chars.indexOf(string.charAt(i));
c[1] = _chars.indexOf(string.charAt(i + 1));
c[2] = _chars.indexOf(string.charAt(i + 2));
c[3] = _chars.indexOf(string.charAt(i + 3));
b[0] = (c[0] << 2) | (c[1] >> 4);
b[1] = ((c[1] & 15) << 4) | (c[2] >> 2);
b[2] = ((c[2] & 3) << 6) | c[3];
flatArr.push(b[0], b[1], b[2]);
}
flatArr = flatArr.slice(0, flatArr.length - (flatArr.length % 16));
return flatArr;
};
// @ts-ignore
if (typeof Array.indexOf === "function") {
// @ts-ignore
_chars = chars;
}
return {
encode: encode,
decode: decode
};
})();
export { enc, dec };

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6" style="width: 23px">
<path style="fill: #ffffff00" stroke-linecap="round" stroke-linejoin="round" d="M21 7.5l-9-5.25L3 7.5m18 0l-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-9v9"></path>
</svg>

Before

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

View file

@ -1,16 +0,0 @@
<svg
style="width: 23px"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-6 h-6"
>
<path
style="fill: #ffffff00"
stroke-linecap="round"
stroke-linejoin="round"
d="M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5 4.5 0 00-6.364-6.364l-4.5 4.5a4.5 4.5 0 001.242 7.244"
/>
</svg>

Before

Width:  |  Height:  |  Size: 449 B

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:bx="https://boxy-svg.com">
<defs>
<linearGradient id="gradient-2" bx:pinned="true">
<stop offset="0" style="stop-color: rgb(189, 177, 251);"/>
<stop offset="1" style="stop-color: rgb(56, 40, 137);"/>
</linearGradient>
<linearGradient id="gradient-2-0" gradientUnits="userSpaceOnUse" x1="249.128" y1="108.223" x2="249.128" y2="387.839" gradientTransform="matrix(1, 0, 0, 1, 0.522352, -0.522402)" xlink:href="#gradient-2"/>
</defs>
<path d="M 461.989 195.13 C 471.654 233.897 435.897 278.674 375.006 312.516 C 351.223 356.99 303.798 387.317 249.174 387.317 C 220.008 387.317 192.894 378.671 170.336 363.842 C 100.132 362.771 47.029 339.987 37.312 301.014 C 27.762 262.712 62.552 218.542 122.115 184.85 C 145.467 139.098 193.594 107.701 249.174 107.701 C 279.078 107.701 306.825 116.79 329.716 132.314 C 399.547 133.535 452.307 156.296 461.989 195.13 Z M 70.452 301.149 C 76.408 321.921 103.025 334.591 140.998 338.165 C 119.847 313.756 107.08 282.1 107.08 247.509 C 107.08 243.191 107.279 238.92 107.668 234.702 C 79.316 257.185 64.695 281.071 70.452 301.149 Z M 432.485 197.337 C 426.434 176.235 399.064 163.495 360.125 160.157 C 379.612 184.088 391.268 214.459 391.268 247.509 C 391.268 254.474 390.75 261.32 389.751 268.012 C 421.698 244.324 438.604 218.679 432.485 197.337 Z M 277.23 306.128 L 267.075 244.49 C 275.532 239.753 279.17 228.619 279.17 218.221 C 279.17 202.927 263.66 189.485 248.397 189.485 C 233.133 189.485 219.189 203.972 219.189 219.266 C 219.189 229.664 223.35 238.709 231.809 243.445 L 217.992 306.65 Z" style="stroke: rgb(0, 0, 0); fill: url('#gradient-2-0'); stroke-width: 0px; stroke-opacity: 0.59;"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="27.68" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 296"><path fill="#673AB8" d="m128 0l128 73.9v147.8l-128 73.9L0 221.7V73.9z"></path><path fill="#FFF" d="M34.865 220.478c17.016 21.78 71.095 5.185 122.15-34.704c51.055-39.888 80.24-88.345 63.224-110.126c-17.017-21.78-71.095-5.184-122.15 34.704c-51.055 39.89-80.24 88.346-63.224 110.126Zm7.27-5.68c-5.644-7.222-3.178-21.402 7.573-39.253c11.322-18.797 30.541-39.548 54.06-57.923c23.52-18.375 48.303-32.004 69.281-38.442c19.922-6.113 34.277-5.075 39.92 2.148c5.644 7.223 3.178 21.403-7.573 39.254c-11.322 18.797-30.541 39.547-54.06 57.923c-23.52 18.375-48.304 32.004-69.281 38.441c-19.922 6.114-34.277 5.076-39.92-2.147Z"></path><path fill="#FFF" d="M220.239 220.478c17.017-21.78-12.169-70.237-63.224-110.126C105.96 70.464 51.88 53.868 34.865 75.648c-17.017 21.78 12.169 70.238 63.224 110.126c51.055 39.889 105.133 56.485 122.15 34.704Zm-7.27-5.68c-5.643 7.224-19.998 8.262-39.92 2.148c-20.978-6.437-45.761-20.066-69.28-38.441c-23.52-18.376-42.74-39.126-54.06-57.923c-10.752-17.851-13.218-32.03-7.575-39.254c5.644-7.223 19.999-8.261 39.92-2.148c20.978 6.438 45.762 20.067 69.281 38.442c23.52 18.375 42.739 39.126 54.06 57.923c10.752 17.85 13.218 32.03 7.574 39.254Z"></path><path fill="#FFF" d="M127.552 167.667c10.827 0 19.603-8.777 19.603-19.604c0-10.826-8.776-19.603-19.603-19.603c-10.827 0-19.604 8.777-19.604 19.603c0 10.827 8.777 19.604 19.604 19.604Z"></path></svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

61
src/components/Card.astro Normal file
View file

@ -0,0 +1,61 @@
---
interface Props {
title: string;
body: string;
href: string;
}
const { href, title, body } = Astro.props;
---
<li class="link-card">
<a href={href}>
<h2>
{title}
<span>&rarr;</span>
</h2>
<p>
{body}
</p>
</a>
</li>
<style>
.link-card {
list-style: none;
display: flex;
padding: 1px;
background-color: #23262d;
background-image: none;
background-size: 400%;
border-radius: 7px;
background-position: 100%;
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
}
.link-card > a {
width: 100%;
text-decoration: none;
line-height: 1.4;
padding: calc(1.5rem - 1px);
border-radius: 8px;
color: white;
background-color: #23262d;
opacity: 0.8;
}
h2 {
margin: 0;
font-size: 1.25rem;
transition: color 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
p {
margin-top: 0.5rem;
margin-bottom: 0;
}
.link-card:is(:hover, :focus-within) {
background-position: 0;
background-image: var(--accent-gradient);
}
.link-card:is(:hover, :focus-within) h2 {
color: rgb(var(--accent-light));
}
</style>

View file

@ -0,0 +1,39 @@
---
import { getLangFromUrl, useTranslations } from "../i18n/utils";
import Logo from "./Logo.astro";
import HeaderButton from "./HeaderButton.astro";
import { Icon } from "astro-icon/components";
const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
---
<div
id="navbar"
class="flex h-16 flex-row items-center justify-end border-b-2 border-border-color bg-navbar-color px-4"
>
<a href="/" class="w-1/8">
<div class="relative flex flex-row">
<HeaderButton text="Games">
<Icon
name="ph:cube"
class="h-6 w-6 text-text-color transition duration-500 group-hover:text-text-hover-color md:h-6 md:w-6"
/>
{
/* Astro won't let us pass the icon as a prop so it's going into the outlet here. */
}
</HeaderButton>
<HeaderButton text={t("faq.whatIsAProxy")}>
<Icon
name="ph:wrench-fill"
class="h-6 w-6 text-text-color transition duration-500 group-hover:text-text-hover-color md:h-6 md:w-6"
/>
</HeaderButton>
<HeaderButton text="Want more links?">
<Icon
name="ph:link-bold"
class="h-6 w-6 text-text-color transition duration-500 group-hover:text-text-hover-color md:h-6 md:w-6"
/>
</HeaderButton>
</div>
</a>
</div>

View file

@ -1,128 +0,0 @@
import { HeaderButton } from "./HeaderButton";
import { useTranslation } from "react-i18next";
import { Link } from "preact-router";
import { motion } from "framer-motion";
// Header icons
import { HiOutlineCube } from "react-icons/hi";
import { RxMixerVertical, RxHamburgerMenu } from "react-icons/rx";
import { RiLinksFill } from "react-icons/ri";
import { BsQuestionLg } from "react-icons/bs";
import { useState } from "preact/hooks";
import { Logo } from "./logo";
export function Header() {
const { t } = useTranslation();
const [isActive, setIsActive] = useState(false);
return (
<div
id="navbar"
className="flex h-16 flex-row items-center justify-between border-b-2 border-border-color bg-navbar-color px-4 "
>
<Link href="/" className="w-1/8">
<div className="relative flex flex-row items-center">
<div className="h-16 w-16 fill-navbar-text-color transition-all duration-1000 hover:rotate-[360deg]">
<Logo />
</div>
<h1 className="font-roboto invisible whitespace-nowrap text-2xl font-bold text-navbar-text-color sm:visible sm:text-4xl">
{" "}
{t("header.title")}{" "}
</h1>
</div>
</Link>
<motion.button
type="button"
className="text-bold z-20 mr-4 text-3xl text-text-color md:hidden"
aria-expanded={isActive}
aria-controls="navbar-default"
onClick={() => setIsActive(!isActive)}
initial={{ rotate: 0 }}
animate={{ rotate: isActive ? 90 : 0 }}
transition={{ duration: 0.5 }}
>
<RxHamburgerMenu />
</motion.button>
{window.innerWidth >= 768 && (
//standard menu
<div
className={`fixed inset-0 z-10 flex md:relative md:right-0 ${
window.innerWidth <= 768 && !isActive && "hidden"
}`}
>
<div className="mt-16 h-[calc(100%-4rem)] w-full md:mt-auto md:h-full lg:mt-auto lg:h-full">
<div
className="flex h-full w-full whitespace-nowrap"
onClick={() => setIsActive(false)}
>
<div className="flex w-full flex-col justify-evenly md:flex-row">
<HeaderButton
href="/games"
Icon={HiOutlineCube}
translationKey="header.games"
/>
<HeaderButton
href="/settings"
Icon={RxMixerVertical}
translationKey="header.settings"
/>
<HeaderButton
href="/faq"
Icon={BsQuestionLg}
translationKey="header.faq"
/>
<HeaderButton
href="/discord"
Icon={RiLinksFill}
translationKey="header.discord"
/>
</div>
</div>
</div>
</div>
)}
{window.innerWidth <= 768 && (
//animated mobile menu
<motion.div
className={`fixed inset-0 z-10 flex md:relative md:right-0 ${
window.innerWidth <= 768 && !isActive && "hidden"
}`}
initial={{ x: 0 }}
animate={{ x: isActive ? 0 : 1000 }}
transition={{ duration: 0.5 }}
>
<div className="mt-16 h-[calc(100%-4rem)] w-full md:mt-auto md:h-full lg:mt-auto lg:h-full">
<div
className="flex h-full w-full"
onClick={() => setIsActive(false)}
>
<div className="flex w-full flex-col justify-evenly md:flex-row">
<HeaderButton
href="/games"
Icon={HiOutlineCube}
translationKey="header.games"
/>
<HeaderButton
href="/settings"
Icon={RxMixerVertical}
translationKey="header.settings"
/>
<HeaderButton
href="/faq"
Icon={BsQuestionLg}
translationKey="header.faq"
/>
<HeaderButton
href="/discord"
Icon={RiLinksFill}
translationKey="header.discord"
/>
</div>
</div>
</div>
</motion.div>
)}
</div>
);
}

View file

@ -0,0 +1,14 @@
---
const { text, icon } = Astro.props;
---
<div
class="group flex w-full flex-row items-center justify-center border-t-2 border-solid border-navbar-text-color p-4 md:border-none"
>
<slot />
<span
class="font-roboto pl-2 text-center text-3xl font-bold text-text-color roboto transition duration-500 group-hover:text-text-hover-color md:text-xl text-nowrap"
>
{text}
</span>
</div>

View file

@ -1,24 +0,0 @@
import { useTranslation } from "react-i18next";
import { Link } from "preact-router";
interface HeaderButtonProps {
href: string;
Icon: any;
translationKey: string;
}
export function HeaderButton(props: HeaderButtonProps) {
const { href, Icon, translationKey } = props;
const { t } = useTranslation();
return (
<Link href={href} className="flex h-full w-full bg-navbar-color sm:h-auto">
<div className="group flex w-full flex-row items-center justify-center border-t-2 border-solid border-navbar-text-color p-4 md:border-none">
<Icon className="h-6 w-6 text-text-color transition duration-500 group-hover:text-text-hover-color md:h-6 md:w-6" />
<span className="font-roboto pl-1 text-center text-3xl font-bold text-text-color transition duration-500 group-hover:text-text-hover-color md:text-lg">
{t(translationKey)}
</span>
</div>
</Link>
);
}

View file

@ -1,12 +0,0 @@
import { Header } from "./Header";
export function HeaderRoute(props: { children: any }) {
return (
<div className="flex h-screen flex-col">
<Header />
<div className="flex-1 overflow-y-auto overflow-x-hidden">
<main className="h-full">{props.children}</main>
</div>
</div>
);
}

18
src/components/Logo.astro Normal file
View file

@ -0,0 +1,18 @@
<svg
version="1.2"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 400 400"
width="400"
height="400"
style="width: 100%; height: 100%;"
>
<title>nebula</title>
<g id="svgg">
<path
id="path0"
fill-rule="evenodd"
class="s0"
d="m213.6 84c1 0.3 3.4 0.7 5.1 1 1.8 0.2 4.1 0.7 5.2 1 13.2 4.1 20.3 6.8 24.5 9.1 0.6 0.3 2.3 1.2 3.8 2 2.8 1.4 13.1 8 14.4 9.2 0.5 0.3 2.3 1.9 4.2 3.5 6.7 5.5 15.5 14.9 19.2 20.4 1 1.4 2 2.7 2.2 2.8 0.3 0.1 0.5 0.5 0.5 0.8 0 0.3 1.2 2.2 2.5 4.3 2.3 3.4 7.8 14.3 9.8 19.3 0.8 2.1 0.9 2.2 10 4.9 5.6 1.6 11.1 3.4 11.7 3.8 0.3 0.2 2.4 1.1 4.7 1.9 11.1 4.1 23 12.5 27.3 19.4 5.5 8.7 3.6 20.5-4.5 28.5-3.1 3-7.5 6.4-8.4 6.4-0.3 0-0.7 0.2-0.8 0.5-1.1 2.3-23.3 11.2-35.9 14.3-3.2 0.9-3.5 1.2-5.7 6.8-7.5 19-25.5 40.6-42.3 51.1-1.6 1-3.1 2-3.3 2.2-0.1 0.2-0.9 0.7-1.7 1.2-0.8 0.4-2.2 1.2-2.9 1.6-0.8 0.4-1.6 1-1.8 1.1-0.5 0.5-4.1 2.2-8.1 3.9-1.8 0.8-3.8 1.6-4.5 2-0.7 0.3-3.1 1.1-5.2 1.8-8.3 2.6-9.8 3-21 4.9-6.4 1.1-25.3 1.3-30.5 0.3-1.9-0.3-5.8-1.1-8.6-1.6-6.8-1.3-12.7-3-20-5.7-3.3-1.2-18-8.8-19.7-10.1-0.9-0.7-4.1-3.1-7.1-5.2-5.7-4.1-17.9-15.9-20.7-20-0.9-1.2-2-2.7-2.5-3.2-3.2-3.3-13.7-21.7-13.7-24.1 0-0.6-0.2-1.2-0.6-1.4-0.3-0.2-0.8-1.2-1-2.3-0.4-1.9-1.7-2.7-6.5-3.8-23.2-5.6-43.1-17.2-48.6-28.5-7.1-14.4 4.5-31.3 27.3-39.7 1.8-0.7 4.1-1.6 5.2-2 3.7-1.5 8-2.9 19.5-6.2 1.6-0.5 2.8-1.2 2.8-1.7 0-2.4 9.8-21.6 13.1-25.7 0.2-0.4 1.4-1.8 2.5-3.2 13.8-18.1 30.2-30.6 50.6-38.8 4.3-1.7 6-2.3 14.3-4.5 5.5-1.6 11.2-2.4 18.4-2.9 7.9-0.5 24-0.2 26.8 0.6zm-29.7 17.3c-0.2 0.1-7.3 1.7-12.9 2.7-1.7 0.4-4.3 1.2-5.8 1.9-1.5 0.6-3.9 1.5-5.5 2-1.5 0.4-3.3 1.3-4 2-0.7 0.7-1.7 1.2-2.3 1.2-1.2 0-9.5 4.5-9.8 5.3-0.2 0.3-0.5 0.6-0.9 0.6-1.9 0-19.6 16-23.8 21.6-9.3 12.2-16.1 27.4-19.2 42.7-2 10.1-1.1 37.5 1.4 41.4 0 0.1 3.7 0.9 8.1 1.8 9.5 1.9 12.8 2.4 34.6 4.9 38.5 4.5 107.9 2.2 138.3-4.5 1.4-0.3 4.1-0.9 6.1-1.3 4.2-0.8 3.4 0.2 4.9-7.1 1.6-8.3 1.7-27 0.1-34.6-1.5-7.1-3.2-13.4-3.7-14.2-0.3-0.4-0.7-1.5-0.9-2.6-2.8-12.1-19.2-34.1-33-44.3-2.9-2.1-5.5-4-5.8-4.3-2.8-2.2-4.9-3.1-7.2-3.1-2.1 0-2.6-0.2-2.7-1.3-0.2-1.5-5.7-4.5-6.3-3.5-0.7 1.1-2.4 0.6-2.7-0.7-0.4-1.3-1.2-1.6-5.8-2.1-1.6-0.2-4-0.9-5.5-1.6-3.9-1.8-5.3-2.2-10.2-2.6-4.6-0.4-25.2-0.7-25.5-0.3zm74.3 42.2c7.4 9.8 4.8 23.5-4.6 24.9-6.9 1-20.9-5.8-21-10.2 0-0.2-0.3-0.8-0.8-1.3-6.4-6.8-5-20.8 2.4-24.1 6.7-2.9 17.2 1.8 24 10.7zm-176.4 36.4c-0.1-0.1-4.6 1.1-5.9 1.6-0.7 0.3-3 1.2-5.1 2-9.9 3.8-15.1 6.8-19.6 11.5-3.4 3.5-3.3 4.5 0.5 8.7 1 1 11.3 7.6 12 7.6 0.2 0 1.7 0.6 3.4 1.3 1.6 0.8 3.6 1.6 4.3 1.9 1.8 0.8 9.3 3.3 9.9 3.3 0.3 0 0.3-2 0-4.4-0.6-5.6-0.6-24.5 0.1-29.6 0.3-2.1 0.5-3.9 0.4-3.9zm229.3-0.3c-0.2 0 0 1 0.2 2.1 0.6 2.8 0.6 31.3 0 34-0.5 2.2-0.4 2.2 1.3 1.8 3.1-0.7 12.9-4.5 18.3-7 8.5-4 14.3-10.1 12.6-13.3-1.1-2.1-6.7-7.2-7.9-7.2-0.4 0-0.9-0.2-1-0.5-0.4-1.1-11.8-6.1-19.2-8.5-2.3-0.7-4.2-1.4-4.3-1.4zm-199.4 63.4l-3.1-0.4 1.8 3.2c0.9 1.8 1.9 3.4 2.2 3.5 0.3 0.1 0.5 0.6 0.5 1.1 0 0.4 0.6 1.5 1.3 2.3 0.7 0.9 1.5 1.9 1.8 2.2 0.3 0.4 0.8 1.2 1.1 1.7 6.2 10.7 35.6 33.5 43.3 33.5 0.2 0 1.3 0.4 2.5 0.9 2.5 1.2 10.6 3.4 15.3 4.2 9.5 1.8 11.6 2.1 17.4 2.1 6.6 0 16.4-1.3 22.9-3 2.2-0.5 5.2-1.3 6.8-1.7 1.6-0.3 3.2-0.9 3.5-1.2 0.4-0.3 1.1-0.6 1.6-0.6 2.3 0 22-10.6 24-12.9 0.2-0.2 2.2-1.9 4.5-3.7 5.7-4.5 11.8-11 17.1-18.4 1.6-2.3 3.2-4.5 3.6-4.9 0.4-0.4 0.7-1 0.7-1.2 0-0.2 0.8-1.9 1.9-3.6 1.1-1.7 1.9-3.2 1.9-3.4 0-0.2-3.8 0.4-11 1.6-31.7 5.4-85.1 6.7-126.9 3.1-9.6-0.8-23.1-2.3-27.8-3.2-2.2-0.4-5.3-0.9-6.9-1.2z"
></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View file

@ -1,88 +0,0 @@
import { Helmet } from "react-helmet";
export default function Meta() {
return (
<Helmet>
<meta name="googlebot" content="index, follow, snippet" />
<link rel="canonical" href="https://nebulaproxy.io/" />
<meta
name="keywords"
content="proxy, web proxy, unblock websites, unblock chromebook, free web proxy, proxy list, proxy sites, un block chromebook, online proxy, proxy server, proxysite, proxy youtube, bypass securly, bypass iboss, bypass lightspeed filter, chromebooks, titanium network, unblock youtube, youtube proxy, unblocked youtube, youtube unblocked"
/>
<meta
name="description"
content="NebulaWeb is an official flagship of Nebula Services and Nebula Developer Labs. NebulaWeb is a stunning, sleek, and functional web-proxy with support for thousands of popular sites. With NebulaWeb, the sky is the limit."
/>
<meta property="og:site_name" content="Nebula" />
<meta property="og:url" content="https://nebulaproxy.io/" />
<meta property="og:title" content="Nebula" />
<meta property="og:image" content="/logo.png" />
<meta property="og:image:secure_url" content="/logo.png" />
<meta property="og:type" content="website" />
<meta name="color-scheme" content="light dark" />
<meta property="og:title" content="Nebula" />
<meta
content="A stunning and sleak web proxy frontend with support for hundreds of popular sites."
property="og:description"
/>
<meta name="theme-color" content="#191724" />
<script type="application/ld+json">
{`
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Nebula",
"url": "https://nebulaproxy.io",
"sameAs": [
"https://github.com/NebulaServices",
"https://nebulaproxy.io"
]
}
`}
</script>
<script type="application/ld+json">
{`
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [{
"@type": "Question",
"name": "How do I get more links?",
"acceptedAnswer": {
"@type": "Answer",
"text": "You can more links by joining our discord server at discord.gg/unblocker"
}
}, {
"@type": "Question",
"name": "How can I self host Nebula?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Instructions for self hosting Nebula can be found at our GitHub repository."
}
}, {
"@type": "Question",
"name": "What sites can I unblock with Nebula?",
"acceptedAnswer": {
"@type": "Answer",
"text": "With Nebula you can access sites such as Discord, Spotify, YouTube and other game sites!"
}
}, {
"@type": "Question",
"name": "Does Nebula hide my search history?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes! Change your Tab appearance in Settings and make the tab look like another site!."
}
}, {
"@type": "Question",
"name": "Is Nebula open-source?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes! Check out our GitHub where you can deploy or host your own instance of Nebula."
}
}]
}
`}
</script>
</Helmet>
);
}

View file

@ -1,92 +0,0 @@
import { createContext } from "preact";
import { useContext, useEffect, useState } from "preact/hooks";
type Theme =
| "main"
| "hacker"
| "catppuccin-mocha"
| "catppuccin-macchiato"
| "catppuccin-frappe"
| "catppuccin-latte";
const themes: Theme[] = [
"main",
"hacker",
"catppuccin-mocha",
"catppuccin-macchiato",
"catppuccin-frappe",
"catppuccin-latte"
];
type ThemeProviderProps = {
children: React.ReactNode;
};
type ThemeProviderState = {
themes: Theme[];
theme: Theme;
background: string;
setTheme: (theme: Theme) => void;
setBackground: (background: string) => void;
};
const initialState: ThemeProviderState = {
themes: themes,
theme: "main",
background: "",
setTheme: () => null,
setBackground: () => null
};
const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
const defaultTheme = "main";
const storageKey = "theme";
const bgKey = "background";
const [theme, setTheme] = useState<Theme>(
() => (localStorage.getItem(storageKey) as Theme) || defaultTheme
);
const [background, setBackground] = useState<string>(() =>
localStorage.getItem(bgKey)
);
useEffect(() => {
const root = window.document.documentElement;
themes.forEach((theme) => {
root.classList.remove(theme);
});
root.classList.add(theme);
if (background) {
document.documentElement.style.setProperty(
"--background-image",
`url(${background})`
);
}
}, [theme, themes, background]);
const value = {
theme,
themes,
background,
setTheme: (theme: Theme) => {
localStorage.setItem(storageKey, theme);
setTheme(theme);
},
setBackground: (background: string) => {
localStorage.setItem(bgKey, background);
setBackground(background);
}
};
return (
<ThemeProviderContext.Provider {...props} value={value}>
{children}
</ThemeProviderContext.Provider>
);
}
export const useTheme = () => {
const context = useContext(ThemeProviderContext);
if (context === undefined)
throw new Error("useTheme must be used within a ThemeProvider");
return context;
};

View file

@ -1,23 +0,0 @@
import { motion } from "framer-motion";
import { IframeHeader } from "./IframeHeader";
export function Iframe(props: { url: string }) {
return (
<>
<IframeHeader url={props.url} />
<motion.div
className="h-[calc(100%_-_4rem)] w-full select-none"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.5 }}
>
<iframe
id="iframe"
src={props.url}
className="h-full w-full border-none bg-primary"
/>
</motion.div>
</>
);
}

View file

@ -1,194 +0,0 @@
import { useState, useEffect } from "preact/hooks";
import CryptoJS from "crypto-js";
import { useTranslation } from "react-i18next";
import { dec } from "../../aes";
import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import { Link } from "preact-router";
import { RiPictureInPictureExitFill, RiFullscreenFill } from "react-icons/ri";
import {
IoCodeSlashSharp,
IoChevronBackSharp,
IoChevronForwardSharp,
IoReloadSharp
} from "react-icons/io5";
import { FaShareAlt, FaPencilAlt } from "react-icons/fa";
import { FaXmark } from "react-icons/fa6";
interface ProxyFrame extends HTMLElement {
contentWindow: any;
contentDocument: any;
}
function Clipboard(text) {
var textarea = document.createElement("textarea"); // hacky
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
textarea.setSelectionRange(0, 99999);
document.execCommand("copy");
document.body.removeChild(textarea);
}
export function IframeHeader(props: { url: string }) {
const localProxy = localStorage.getItem("proxy") || "automatic";
const { t } = useTranslation();
const share = () => {
let proxyFrame: ProxyFrame | null = document.getElementById(
"iframe"
) as ProxyFrame;
if (localProxy === "ultraviolet" || localProxy === "automatic") {
var encodedUrl = proxyFrame.contentWindow.location.href
.replace(window.location.origin, "")
.replace(window.__uv$config.prefix, "");
//@ts-ignore
let decodedUrl = CryptoJS.AES.decrypt(
"U2FsdGVkX1" + encodedUrl,
location.origin + navigator.userAgent
).toString(CryptoJS.enc.Utf8);
Clipboard(decodedUrl);
toast(t("clipboard"));
} else {
toast("Your proxy choice doesn't support sharing.");
}
};
const [showPopout, setShowPopout] = useState(false);
const [showFullScreen, setFullScreen] = useState(false);
const [proxiedTitle, setProxiedTitle] = useState("");
const [proxiedFavicon, setProxiedFavicon] = useState("/generic_globe.png");
useEffect(() => {
const intervalFunction = () => {
let proxyFrame: ProxyFrame | null = document.getElementById(
"iframe"
) as ProxyFrame;
if (proxyFrame) {
let faviconLink =
proxyFrame.contentDocument.querySelector("link[rel*='icon']");
if (faviconLink) {
setProxiedFavicon(faviconLink.href);
} else {
setProxiedFavicon("/generic_globe.png");
}
setProxiedTitle(proxyFrame.contentDocument.title);
} else {
console.log(
"ehhh this aint supposed to happen. no proxied iframe found."
);
}
};
const intervalId = setInterval(intervalFunction, 1000);
return () => clearInterval(intervalId);
}, []);
if (showPopout) {
window.location.replace(props.url);
}
if (showFullScreen) {
document.getElementById("iframe").requestFullscreen();
setFullScreen(false);
}
return (
<div>
<div
id="iframeNav"
className="flex h-16 flex-row items-center justify-between gap-3 border-b-2 border-border-color bg-navbar-color px-4"
>
<div className="w-1/8">
<div className="flex flex-row items-center">
<img src={proxiedFavicon} className="h-12 w-12 p-2"></img>
<h1 className="font-roboto whitespace-normal text-sm font-bold text-text-color sm:visible sm:text-lg">
{proxiedTitle ? proxiedTitle : "Loading..."}
</h1>
</div>
</div>
<div id="navItems" className="w-1/8">
<div className="mr-4 flex flex-row items-center justify-end gap-3">
<IoChevronBackSharp
className="duration-0500 h-6 w-6 cursor-pointer text-navbar-text-color transition-all hover:scale-110 hover:brightness-125"
onClick={() => {
const proxyFrame: ProxyFrame | null = document.getElementById(
"iframe"
) as ProxyFrame;
proxyFrame.contentWindow.history.back();
}}
/>
<IoChevronForwardSharp
className="duration-0500 h-6 w-6 cursor-pointer text-navbar-text-color transition-all hover:scale-110 hover:brightness-125"
onClick={() => {
const proxyFrame: ProxyFrame | null = document.getElementById(
"iframe"
) as ProxyFrame;
proxyFrame.contentWindow.history.forward();
}}
/>
<IoReloadSharp
className="duration-0500 h-6 w-6 cursor-pointer text-navbar-text-color transition-all hover:rotate-[360deg] hover:scale-110 hover:brightness-125"
onClick={() => {
const proxyFrame: ProxyFrame | null = document.getElementById(
"iframe"
) as ProxyFrame;
proxyFrame.contentWindow.location.reload();
}}
/>
<FaShareAlt
className="duration-0500 h-6 w-6 cursor-pointer text-navbar-text-color transition-all hover:scale-110 hover:brightness-125"
onClick={share}
/>
<IoCodeSlashSharp
className="duration-0500 h-6 w-6 cursor-pointer text-navbar-text-color transition-all hover:scale-110 hover:brightness-125"
onClick={() => {
const proxyFrame: ProxyFrame | null = document.getElementById(
"iframe"
) as ProxyFrame;
if (!proxyFrame) return;
const proxyWindow = proxyFrame.contentWindow;
const proxyDocument = proxyFrame.contentDocument;
if (!proxyWindow || !proxyDocument) return;
if (proxyWindow.eruda?._isInit) {
proxyWindow.eruda.destroy();
} else {
let script = proxyDocument.createElement("script");
script.src = "https://cdn.jsdelivr.net/npm/eruda";
script.onload = function () {
if (!proxyWindow) return;
proxyWindow.eruda.init({
defaults: {
displaySize: 45,
theme: "Material Palenight"
}
});
proxyWindow.eruda.show();
};
proxyDocument.head.appendChild(script);
}
}}
/>
<RiPictureInPictureExitFill
className="duration-0500 h-6 w-6 cursor-pointer text-navbar-text-color transition-all hover:scale-110 hover:brightness-125"
onClick={() => setShowPopout(true)}
/>
<RiFullscreenFill
className="duration-0500 h-6 w-6 cursor-pointer text-navbar-text-color transition-all hover:scale-110 hover:brightness-125 active:rotate-90"
onClick={() => setFullScreen(true)}
/>
<Link href="/">
<FaXmark className="duration-0500 h-6 w-6 cursor-pointer text-navbar-text-color transition-all hover:rotate-[360deg] hover:scale-110 hover:brightness-125" />
</Link>
</div>
</div>
</div>
<ToastContainer position="bottom-right" type="sucess" theme="dark" />
</div>
);
}

View file

@ -1,22 +0,0 @@
export const Logo = () => {
return (
<svg
version="1.2"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 400 400"
width="400"
height="400"
style="width: 100%; height: 100%;"
>
<title>nebula</title>
<g id="svgg">
<path
id="path0"
fill-rule="evenodd"
class="s0"
d="m213.6 84c1 0.3 3.4 0.7 5.1 1 1.8 0.2 4.1 0.7 5.2 1 13.2 4.1 20.3 6.8 24.5 9.1 0.6 0.3 2.3 1.2 3.8 2 2.8 1.4 13.1 8 14.4 9.2 0.5 0.3 2.3 1.9 4.2 3.5 6.7 5.5 15.5 14.9 19.2 20.4 1 1.4 2 2.7 2.2 2.8 0.3 0.1 0.5 0.5 0.5 0.8 0 0.3 1.2 2.2 2.5 4.3 2.3 3.4 7.8 14.3 9.8 19.3 0.8 2.1 0.9 2.2 10 4.9 5.6 1.6 11.1 3.4 11.7 3.8 0.3 0.2 2.4 1.1 4.7 1.9 11.1 4.1 23 12.5 27.3 19.4 5.5 8.7 3.6 20.5-4.5 28.5-3.1 3-7.5 6.4-8.4 6.4-0.3 0-0.7 0.2-0.8 0.5-1.1 2.3-23.3 11.2-35.9 14.3-3.2 0.9-3.5 1.2-5.7 6.8-7.5 19-25.5 40.6-42.3 51.1-1.6 1-3.1 2-3.3 2.2-0.1 0.2-0.9 0.7-1.7 1.2-0.8 0.4-2.2 1.2-2.9 1.6-0.8 0.4-1.6 1-1.8 1.1-0.5 0.5-4.1 2.2-8.1 3.9-1.8 0.8-3.8 1.6-4.5 2-0.7 0.3-3.1 1.1-5.2 1.8-8.3 2.6-9.8 3-21 4.9-6.4 1.1-25.3 1.3-30.5 0.3-1.9-0.3-5.8-1.1-8.6-1.6-6.8-1.3-12.7-3-20-5.7-3.3-1.2-18-8.8-19.7-10.1-0.9-0.7-4.1-3.1-7.1-5.2-5.7-4.1-17.9-15.9-20.7-20-0.9-1.2-2-2.7-2.5-3.2-3.2-3.3-13.7-21.7-13.7-24.1 0-0.6-0.2-1.2-0.6-1.4-0.3-0.2-0.8-1.2-1-2.3-0.4-1.9-1.7-2.7-6.5-3.8-23.2-5.6-43.1-17.2-48.6-28.5-7.1-14.4 4.5-31.3 27.3-39.7 1.8-0.7 4.1-1.6 5.2-2 3.7-1.5 8-2.9 19.5-6.2 1.6-0.5 2.8-1.2 2.8-1.7 0-2.4 9.8-21.6 13.1-25.7 0.2-0.4 1.4-1.8 2.5-3.2 13.8-18.1 30.2-30.6 50.6-38.8 4.3-1.7 6-2.3 14.3-4.5 5.5-1.6 11.2-2.4 18.4-2.9 7.9-0.5 24-0.2 26.8 0.6zm-29.7 17.3c-0.2 0.1-7.3 1.7-12.9 2.7-1.7 0.4-4.3 1.2-5.8 1.9-1.5 0.6-3.9 1.5-5.5 2-1.5 0.4-3.3 1.3-4 2-0.7 0.7-1.7 1.2-2.3 1.2-1.2 0-9.5 4.5-9.8 5.3-0.2 0.3-0.5 0.6-0.9 0.6-1.9 0-19.6 16-23.8 21.6-9.3 12.2-16.1 27.4-19.2 42.7-2 10.1-1.1 37.5 1.4 41.4 0 0.1 3.7 0.9 8.1 1.8 9.5 1.9 12.8 2.4 34.6 4.9 38.5 4.5 107.9 2.2 138.3-4.5 1.4-0.3 4.1-0.9 6.1-1.3 4.2-0.8 3.4 0.2 4.9-7.1 1.6-8.3 1.7-27 0.1-34.6-1.5-7.1-3.2-13.4-3.7-14.2-0.3-0.4-0.7-1.5-0.9-2.6-2.8-12.1-19.2-34.1-33-44.3-2.9-2.1-5.5-4-5.8-4.3-2.8-2.2-4.9-3.1-7.2-3.1-2.1 0-2.6-0.2-2.7-1.3-0.2-1.5-5.7-4.5-6.3-3.5-0.7 1.1-2.4 0.6-2.7-0.7-0.4-1.3-1.2-1.6-5.8-2.1-1.6-0.2-4-0.9-5.5-1.6-3.9-1.8-5.3-2.2-10.2-2.6-4.6-0.4-25.2-0.7-25.5-0.3zm74.3 42.2c7.4 9.8 4.8 23.5-4.6 24.9-6.9 1-20.9-5.8-21-10.2 0-0.2-0.3-0.8-0.8-1.3-6.4-6.8-5-20.8 2.4-24.1 6.7-2.9 17.2 1.8 24 10.7zm-176.4 36.4c-0.1-0.1-4.6 1.1-5.9 1.6-0.7 0.3-3 1.2-5.1 2-9.9 3.8-15.1 6.8-19.6 11.5-3.4 3.5-3.3 4.5 0.5 8.7 1 1 11.3 7.6 12 7.6 0.2 0 1.7 0.6 3.4 1.3 1.6 0.8 3.6 1.6 4.3 1.9 1.8 0.8 9.3 3.3 9.9 3.3 0.3 0 0.3-2 0-4.4-0.6-5.6-0.6-24.5 0.1-29.6 0.3-2.1 0.5-3.9 0.4-3.9zm229.3-0.3c-0.2 0 0 1 0.2 2.1 0.6 2.8 0.6 31.3 0 34-0.5 2.2-0.4 2.2 1.3 1.8 3.1-0.7 12.9-4.5 18.3-7 8.5-4 14.3-10.1 12.6-13.3-1.1-2.1-6.7-7.2-7.9-7.2-0.4 0-0.9-0.2-1-0.5-0.4-1.1-11.8-6.1-19.2-8.5-2.3-0.7-4.2-1.4-4.3-1.4zm-199.4 63.4l-3.1-0.4 1.8 3.2c0.9 1.8 1.9 3.4 2.2 3.5 0.3 0.1 0.5 0.6 0.5 1.1 0 0.4 0.6 1.5 1.3 2.3 0.7 0.9 1.5 1.9 1.8 2.2 0.3 0.4 0.8 1.2 1.1 1.7 6.2 10.7 35.6 33.5 43.3 33.5 0.2 0 1.3 0.4 2.5 0.9 2.5 1.2 10.6 3.4 15.3 4.2 9.5 1.8 11.6 2.1 17.4 2.1 6.6 0 16.4-1.3 22.9-3 2.2-0.5 5.2-1.3 6.8-1.7 1.6-0.3 3.2-0.9 3.5-1.2 0.4-0.3 1.1-0.6 1.6-0.6 2.3 0 22-10.6 24-12.9 0.2-0.2 2.2-1.9 4.5-3.7 5.7-4.5 11.8-11 17.1-18.4 1.6-2.3 3.2-4.5 3.6-4.9 0.4-0.4 0.7-1 0.7-1.2 0-0.2 0.8-1.9 1.9-3.6 1.1-1.7 1.9-3.2 1.9-3.4 0-0.2-3.8 0.4-11 1.6-31.7 5.4-85.1 6.7-126.9 3.1-9.6-0.8-23.1-2.3-27.8-3.2-2.2-0.4-5.3-0.9-6.9-1.2z"
/>
</g>
</svg>
);
};

1
src/env.d.ts vendored Normal file
View file

@ -0,0 +1 @@
/// <reference types="astro/client" />

View file

@ -1,32 +0,0 @@
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import translationEN from "./locales/en.json";
import translationES from "./locales/es.json";
import translationJA from "./locales/ja.json";
const resources = {
en: {
translation: translationEN
},
es: {
translation: translationES
},
ja: {
translation: translationJA
}
};
i18n
.use(initReactI18next)
.use(LanguageDetector)
.init({
resources,
fallbackLng: "en",
interpolation: {
escapeValue: false
}
});
export default i18n;

65
src/i18n/en.json Normal file
View file

@ -0,0 +1,65 @@
{
"ultraviolet": "Ultraviolet",
"pages.home": "Home | Alu",
"pages.games": "Games | Alu",
"pages.settings": "Settings | Alu",
"nav.brand": "Alu",
"nav.games": "Games",
"nav.settings": "Settings",
"menu.welcome": "Welcome to Alu",
"menu.search": "Search the web...",
"faq.title": "Frequently Asked Questions",
"faq.whatIsAProxy": "What is a proxy?",
"faq.whatIsAProxy.answer": "A proxy is a method of making your internet traffic anonymous by sending your request to a server (proxy), having that make the request, and then send it back to you! This allows for a much larger level of security, as well as bypassing website restrictions in public spaces and censorship.",
"faq.noBareClients": "What does \"there are no bare clients\" mean?",
"faq.noBareClients.answer": "There are a couple reasons this particular error happens, but it's most commonly an issue with the proxy failing to load. Please reload the page, and if the problem persists, make a GitHub issue!",
"faq.contributeToAlu": "How can I contribute to Alu?",
"faq.contributeToAlu.answer.segment1": "Spreading the word of Alu is a great start, but if you really enjoy Alu, and want private links, consider supporting me through Patreon!",
"faq.contributeToAlu.answer.patreonLinkText": "You can support me here!",
"faq.contributeToAlu.answer.segment2": "Thank you for helping to make Alu great!",
"footer.brand": "Alu",
"footer.madeWithLove": "Made with ❤️ by wearr",
"footer.poweredBy": "Titanium Network",
"footer.services": "Services",
"footer.socials": "Socials",
"footer.aluProject": "Alu Project",
"games.title": "Games",
"games.search": "Search...",
"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.transport": "Transport",
"settings.proxy.wispURL": "Wisp 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.cloaking": "Cloaking",
"settings.cloaking.subtext": "Change how your tab looks...",
"settings.cloaking.updateCloak": "Update Cloak",
"settings.credits": "Credits",
"settings.credits.mochaandmacchiatothemes": "Mocha & Macchiato Themes",
"settings.credits.japaneseTranslations": "Japanese Translations"
}

65
src/i18n/jp.json Normal file
View file

@ -0,0 +1,65 @@
{
"ultraviolet": "Ultraviolet",
"pages.home": "ホーム | Alu",
"pages.games": "ゲーム | Alu",
"pages.settings": "設定 | Alu",
"nav.brand": "Alu",
"nav.games": "ゲーム",
"nav.settings": "設定",
"menu.welcome": "Aluへようこそ",
"menu.search": "検索",
"faq.title": "よくある質問",
"faq.whatIsAProxy": "プロキシとは",
"faq.whatIsAProxy.answer": "プロキシは、インターネットを接続する際に、ネットワークの内部から外部へのアクセスを代理で行うシステムのことです。ウェブサイトにアクセスする際に自分のIPアドレスを隠すことができます。",
"faq.noBareClients": "エラー「there are no bare clients.」とは",
"faq.noBareClients.answer": "このエラーは、通常はプロキシのロードに失敗したことを意味します。ページをリロードするか、GitHubで問題を作成してください。",
"faq.contributeToAlu": "貢献をするには",
"faq.contributeToAlu.answer.segment1": "Alu を広めることは素晴らしいことです。プライベートリンクが必要な場合は、Patreon を通じて私をサポートすることを検討してください。",
"faq.contributeToAlu.answer.patreonLinkText": "ここで私に貢献できます",
"faq.contributeToAlu.answer.segment2": "Aluに貢献していただきありがとうございます。",
"footer.brand": "Alu",
"footer.madeWithLove": "wearrによる❤で作られました",
"footer.poweredBy": "Titanium Network",
"footer.services": "サービス",
"footer.socials": "ソーシャル",
"footer.aluProject": "Alu Project",
"games.title": "ゲーム",
"games.search": "検索...",
"settings.title": "設定",
"settings.proxy": "プロキシ",
"settings.proxy.auto": "自動",
"settings.proxy.selectedProxy": "選択したプロキシ",
"settings.proxy.searchEngine": "検索エンジン",
"settings.proxy.openPageWith": "開く",
"settings.proxy.openPageWith.embed": "埋め込み",
"settings.proxy.openPageWith.newTab": "新しいタブ",
"settings.proxy.searxngURL": "Searx URL",
"settings.proxy.transport": "Transport",
"settings.proxy.wispURL": "Wisp URL",
"settings.proxy.bareURL": "Bare URL",
"settings.customization": "カスタマイズ",
"settings.customization.theme": "テーマ",
"settings.customization.theme.Alu": "Alu",
"settings.customization.theme.Macchiato": "マキアート",
"settings.customization.theme.Mocha": "モカ",
"settings.customization.language": "言語",
"settings.cloaking": "クローキング",
"settings.cloaking.subtext": "タブの見た目を変更します...",
"settings.cloaking.updateCloak": "クロークを更新",
"settings.credits": "クレジット",
"settings.credits.mochaandmacchiatothemes": "モカとマキアートテーマ",
"settings.credits.japaneseTranslations": "日本語翻訳"
}

9
src/i18n/ui.ts Normal file
View file

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

18
src/i18n/utils.ts Normal file
View file

@ -0,0 +1,18 @@
import { ui, defaultLang } from "./ui";
export const STATIC_PATHS = [
{ params: { lang: "en" } },
{ params: { lang: "jp" } },
];
export function getLangFromUrl(url: URL) {
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 ui[lang][key] || ui[defaultLang][key];
};
}

View file

@ -1,77 +0,0 @@
import { render } from "preact";
import { Suspense, lazy } from "preact/compat";
import { LoadSuspense } from "./LoadSuspense";
import Meta from "./components/Meta";
import Particles, { initParticlesEngine } from "@tsparticles/react";
import { loadSlim } from "@tsparticles/slim";
import { useEffect, useState } from "preact/compat";
import { ThemeProvider, useTheme } from "./components/ThemeProvider";
const Routes = lazy(() => import("./routes"));
const particlesUrl = localStorage.getItem("particles") || "none";
export default function App() {
const [init, setInit] = useState(false);
const { background } = useTheme();
// this should be run only once per application lifetime
useEffect(() => {
initParticlesEngine(async (engine) => {
// you can initiate the tsParticles instance (engine) here, adding custom shapes or presets
// this loads the tsparticles package bundle, it's the easiest method for getting everything ready
// starting from v2 you can add only the features you need reducing the bundle size
//await loadAll(engine);
//await loadFull(engine);
await loadSlim(engine);
//await loadBasic(engine);
}).then(() => {
setInit(true);
});
}, []);
const particlesLoaded = (container) => {
console.log(container);
};
return (
<div className={!background && "bg-primary"}>
{window.location.origin === "https://nebulaproxy.io" && <Meta />}
{/* {window.location.origin === "http://localhost:8080" && <Meta />} */}
<Suspense fallback={<LoadSuspense />}>
<div className="fixed z-30 h-full w-full">
<Routes />
</div>
<div
className="fixed z-10 h-full w-full bg-primary"
style={{
backgroundImage: background ? `url(${background})` : "none",
backgroundSize: "cover",
backgroundRepeat: "no-repeat",
backgroundPosition: "center"
}}
>
{init && particlesUrl !== "none" && (
<Particles
id="tsparticles"
url={particlesUrl}
particlesLoaded={particlesLoaded}
className="bg-primary"
/>
)}
</div>
</Suspense>
{/* <video muted autoplay loop className="relative z-10 h-screen w-full object-cover">
<source
src="bg.mp4"
type="video/mp4"
/>
</video> */}
</div>
);
}
render(
<ThemeProvider>
<App />
</ThemeProvider>,
document.getElementById("app")
);

59
src/layouts/Layout.astro Normal file
View file

@ -0,0 +1,59 @@
---
import Header from "../components/Header.astro";
import "../themes/nebula.css";
interface Props {
title: string;
}
const { title } = 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="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
</head>
<body>
<Header />
<slot />
</body>
</html>
<style is:global>
@import "https://fonts.googleapis.com/css2?family=Roboto:wght@100&display=swap";
:root {
--accent: 136, 58, 234;
--accent-light: 224, 204, 250;
--accent-dark: 49, 10, 101;
--accent-gradient: linear-gradient(
45deg,
rgb(var(--accent)),
rgb(var(--accent-light)) 30%,
white 60%
);
}
html {
font-family: system-ui, sans-serif;
background: #13151a;
background-size: 224px;
}
code {
font-family:
Menlo,
Monaco,
Lucida Console,
Liberation Mono,
DejaVu Sans Mono,
Bitstream Vera Sans Mono,
Courier New,
monospace;
}
.roboto {
font-family: Roboto;
}
</style>

View file

@ -1,143 +0,0 @@
{
"header": {
"title": "nebula.",
"games": "Games",
"settings": "Settings",
"discord": "Want more links?",
"faq": "FAQ"
},
"404": {
"text": "This Nebula Service has been disabled.",
"return": "Back to home"
},
"home": {
"placeholder": "Search the web freely"
},
"discord": {
"title": "Nebula's Discord Server",
"sub": "Would you like to open this via proxy?",
"button1": "Open Normally",
"button2": "Use Proxy"
},
"settings": {
"tabs": {
"proxy": "Proxy",
"tab": "Tab",
"custom": "Customization",
"misc": "Misc",
"credits": "Credits"
},
"proxy": {
"title": "Proxy",
"subtitle": "Choose the proxy that fits your needs",
"automatic": "Automatic"
},
"languages": {
"title": "Language",
"subtitle": "Choose your preferred language"
},
"proxymodes": {
"title": "Open in",
"subtitle": "Choose how to open your sites",
"embed": "Embed",
"direct": "Direct",
"aboutblank": "About:Blank"
},
"cloaking": {
"title": "Cloaking",
"subtitle": "Choose how your tab looks",
"aboutblank": "Open in about:blank"
},
"search": {
"title": "Search Engine",
"subtitle": "Choose your search engine"
},
"bare": {
"title": "Bare Server",
"subtitle": "Enter the URL of your bare server",
"select": "Select"
},
"wisp": {
"title": "Wisp Server",
"subtitle": "Enter the url of a Wisp server",
"select": "Select"
},
"transport": {
"title": "Transport",
"desc": "Select the transport to use"
},
"theme": {
"title": "Theme",
"subtitle": "Choose a theme so your eyes don't hate us",
"select": "Select",
"particles": "Particles",
"particlesDesc": "Choose a particles wallpaper",
"particlesNone": "None",
"background": "Background",
"backgroundDesc": "Choose a background image",
"backgroundNone": "None"
},
"httpProxy": {
"title": "(Advanced) HTTP Proxy",
"subtitle": "Enter your own HTTP proxy to access geo-restricted content. This will only work with compatible Bare servers.",
"link": "What is this?",
"badBare": "Your Bare server doesn't support HTTP proxies.",
"badProxy": "Proxy connection failed. Please double check that your HTTP proxy is working.",
"reset": "Reset"
}
},
"titles": {
"home": "Nebula",
"settings": "Nebula - Settings",
"discord": "Nebula - Discord",
"404": "Nebula - 404"
},
"comingsoon": "Coming soon!",
"themes": {
"main": "Main",
"hacker": "Hacker",
"crismas": "Crismas🐴🎄",
"catppuccin-mocha": "Catppuccin Mocha",
"catppuccin-macchiato": "Catppuccin Macchiato",
"catppuccin-frappe": "Catppuccin Frappe",
"catppuccin-latte": "Catppuccin Latte"
},
"clipboard": "URL copied to clipboard!",
"bareError": "That didn't quite work. Double check that the bare server exists and isn't blocked.",
"faq": {
"1": {
"q": "How do I get more links?",
"a": "You can more links by joining our ",
"h": "discord server.",
"hR": "/discord"
},
"2": {
"q": "How can I self host Nebula?",
"a": "Instructions for self hosting Nebula can be found at our ",
"h": "GitHub repository.",
"hR": "https://github.com/NebulaServices/Nebula"
},
"3": {
"q": "Nebula won't work for me, what can I do?",
"a": "Try pressing Ctrl+Shift+R and/or Shift+F5.",
"aMobile": "Try going into settings, clicking on your browser, and clearing cache."
},
"4": {
"q": "What is an HTTP proxy?",
"a": "An HTTP proxy is like a VPN. It can change your IP's location to allow you to access geo-restricted content that only other countries can access."
},
"5": {
"q": "Where can I get an HTTP proxy?",
"a": "You can look up a tutorial on how to run your own or buy one online. HTTP proxies are coming to boosters in our discord server in the near future so be on the lookout."
},
"6": {
"q": "Nebula is slow.",
"a": "You might be connected to the wrong server. Check the bare server you are connected to and make sure its the country you're closest to."
}
},
"credits": {
"devs": "Site Developers",
"jpTranslators": "Japanese Translators",
"esTranslators": "Spanish Translators"
}
}

View file

@ -1,143 +0,0 @@
{
"header": {
"title": "nebula.",
"games": "Juegos",
"settings": "Ajustes",
"discord": "¿Quieres más enlaces?",
"faq": "Preguntas más frecuentes"
},
"404": {
"text": "Este servicio Nebula ha sido deshabilitado.",
"return": "De vuelta a casa"
},
"home": {
"placeholder": "Busca en la web libremente"
},
"discord": {
"title": "Nebula's Discord Servidor",
"sub": "¿Le gustaría abrir esto a través de proxy?",
"button1": "Abierto normalmente",
"button2": "Usa proxy"
},
"settings": {
"tabs": {
"proxy": "Proxy",
"tab": "Pestaña",
"custom": "Personalización",
"misc": "Misc",
"credits": "Créditos"
},
"proxy": {
"title": "Proxy",
"subtitle": "Elige el proxy que se ajuste a tus necesidades",
"automatic": "Automática"
},
"languages": {
"title": "Idioma",
"subtitle": "Elige tu idioma preferido"
},
"proxymodes": {
"title": "Abrir en",
"subtitle": "Elige cómo abrir tus sitios",
"embed": "Incrustar",
"direct": "Directo",
"aboutblank": "About:Blank"
},
"cloaking": {
"title": "Encubrimiento",
"subtitle": "Elige cómo se ve tu pestaña",
"aboutblank": "Abierte en about:blank"
},
"search": {
"title": "Buscador",
"subtitle": "Elige tu motor de búsqueda."
},
"bare": {
"title": "Bare Server",
"subtitle": "Pon tu url de tu bare server",
"select": "Seleccionar"
},
"wisp": {
"title": "Servidor Wisp",
"subtitle": "Introduzca la URL de un servidor Wisp",
"select": "Seleccionar"
},
"transport": {
"title": "Transporte",
"desc": "Seleccione el transporte que desea utilizar"
},
"theme": {
"title": "Mirar",
"subtitle": "Elige una mirada para que tus ojos no nos odienn",
"select": "Seleccionar",
"particles": "Partículas",
"particlesDesc": "Elige un fondo de pantalla de partículas",
"particlesNone": "Ninguna",
"background": "Fondo",
"backgroundDesc": "Elige una imagen de fondo",
"backgroundNone": "Ninguna"
},
"httpProxy": {
"title": "Proxy HTTP (avanzado)",
"subtitle": "Ingrese su propio proxy HTTP para acceder a contenido restringido geográficamente. Esto solo funcionará con servidores Bare compatibles.",
"link": "¿Qué es esto?",
"badBare": "Su servidor Bare no soporta servidores proxy HTTP.",
"badProxy": "Error en la conexión del proxy. Verifique que su proxy HTTP esté funcionando.",
"reset": "Reiniciar"
}
},
"titles": {
"home": "Nebula",
"settings": "Nebula - Ajustes",
"discord": "Nebula - Discord",
"404": "Nebula - 404"
},
"comingsoon": "¡Próximamente!",
"themes": {
"main": "Por defecto",
"hacker": "Hacker",
"crismas": "Naidad🐴🎄",
"catppuccin-mocha": "Catppuccin Mocha",
"catppuccin-macchiato": "Catppuccin Macchiato",
"catppuccin-frappe": "Catppuccin Frappe",
"catppuccin-latte": "Catppuccin Latte"
},
"clipboard": "¡URL copiada al portapapeles!",
"bareError": "Esto no es funcional. Vuelva a verificar que el servidor bare exista y no esté bloqueado.",
"faq": {
"1": {
"q": "¿Cómo puedo obtener más enlaces?",
"a": "Puedes obtener más enlaces uniéndote a nuestro ",
"h": "servidor de discord.",
"hR": "/discord"
},
"2": {
"q": "¿Cómo puedo auto hospedar Nebula?",
"a": "Las instrucciones para auto hospedar Nebula se pueden encontrar en nuestro ",
"h": "repositorio de GitHub.",
"hR": "https://github.com/NebulaServices/Nebula"
},
"3": {
"q": "Nebula no funciona para mí, ¿qué puedo hacer?",
"a": "Intenta presionar Ctrl+Shift+R y/o Shift+F5.",
"aMobile": "Intenta ir a ajustes, haz clic en tu navegador y borra la caché."
},
"4": {
"q": "¿Qué es un proxy HTTP?",
"a": "Un proxy HTTP es como una VPN. Puede cambiar la ubicación de tu IP para permitirte acceder a contenido geo-restringido que solo otros países pueden acceder."
},
"5": {
"q": "¿Dónde puedo conseguir un proxy HTTP?",
"a": "Puedes buscar un tutorial sobre cómo ejecutar el tuyo propio o comprar uno en línea. Los proxies HTTP llegarán a los impulsores en nuestro servidor de discord en un futuro cercano, así que estate atento."
},
"6": {
"q": "Nebula es lento.",
"a": "Es posible que estés conectado al servidor incorrecto. Verifica el servidor bare al que estás conectado y asegúrate de que sea el país más cercano a ti."
}
},
"credits": {
"devs": "Desarrolladores del sitio",
"jpTranslators": "Traductores de japonés",
"esTranslators": "Traductores de español"
}
}

View file

@ -1,143 +0,0 @@
{
"header": {
"title": "Nebula",
"games": "ゲーム",
"settings": "設定",
"discord": "リンク一覧",
"faq": "よくある質問 (FAQ)"
},
"404": {
"text": "この要求は無効です",
"return": "戻る"
},
"home": {
"placeholder": "検索欄"
},
"discord": {
"title": "公式 Discordサーバー",
"sub": "このページを経由して開きますか?",
"button1": "いいえ",
"button2": "はい"
},
"settings": {
"tabs": {
"proxy": "プロキシ",
"tab": "タブ",
"custom": "デザイン",
"misc": "その他",
"credits": "法的情報"
},
"proxy": {
"title": "プロキシ",
"subtitle": "使いたいプロキシを選択してください",
"automatic": "自動"
},
"languages": {
"title": "言語",
"subtitle": "使用する言語を選択してください"
},
"proxymodes": {
"title": "モード",
"subtitle": "閲覧するサイトを開く方式を選択してください",
"embed": "埋め込み",
"direct": "直接",
"aboutblank": "空白のページとして開く"
},
"cloaking": {
"title": "表示偽装",
"subtitle": "タブのアイコンを選択する",
"aboutblank": "空白のページとして開く"
},
"search": {
"title": "検索エンジン",
"subtitle": "検索エンジンを選択してください"
},
"bare": {
"title": "Bareサーバー",
"subtitle": "BareサーバーのURLを入力してください",
"select": "決定"
},
"wisp": {
"title": "Wispサーバ",
"subtitle": "WispサーバのURLを入力してください",
"select": "決定"
},
"transport": {
"title": "トランスポートサーバー",
"desc": "使用するページの転送用のサーバーの種類を指定してください"
},
"theme": {
"title": "壁紙",
"subtitle": "お好みの壁紙を選択してください",
"select": "選択する",
"particles": "パーティクル",
"particlesDesc": "パーティクルの種類を選択してください",
"particlesNone": "なし",
"background": "背景",
"backgroundDesc": "背景画像を選択してください",
"backgroundNone": "なし"
},
"httpProxy": {
"title": "HTTPプロキシ (上級者向け)",
"subtitle": "HTTPプロキシのアドレスを入力してください この機能はHTTPプロキシと互換性のあるBareサーバーでのみ機能します",
"link": "資料...HTTPプロキシについて - よくある質問 (FAQ)",
"badBare": "このサーバーはHTTPプロキシに対応していません",
"badProxy": "接続に失敗しました HTTPプロキシが動作している事を確認の上、再度お試しください",
"reset": "リセット"
}
},
"titles": {
"home": "Nebula",
"settings": "Nebula - 設定",
"discord": "Nebula - Discord",
"404": "Nebula - ページが存在しません"
},
"comingsoon": "近日公開",
"themes": {
"main": "標準",
"hacker": "Hacker",
"crismas": "Crismas🐴🎄",
"catppuccin-mocha": "Catppuccin Mocha",
"catppuccin-macchiato": "Catppuccin Macchiato",
"catppuccin-frappe": "Catppuccin Frappe",
"catppuccin-latte": "Catppuccin Latte"
},
"clipboard": "コピーしました!",
"bareError": "処理を実行できませんでした Bareサーバーが存在し、そしてアクセスが拒否されていない事も確認してから、再度お試してください",
"faq": {
"1": {
"q": "公式サイトのリンクはどこですか?",
"a": "Nebula公式によるDiscordサーバーがあります 右のリンクからアクセスしてください",
"h": "Discordサーバー",
"hR": "/discord"
},
"2": {
"q": "Nebulaをオンプレミスとして実行するにはどうすればいいですか",
"a": "Nebulaは右のGithubのリンクからソースコードを入手できます",
"h": "GitHub",
"hR": "https://github.com/NebulaServices/Nebula"
},
"3": {
"q": "Nebulaが正常に動作しません、どうすればいいですか",
"a": "Shift+F5または右上の更新ボタンを押してください",
"aMobile": "設定からキャッシュのクリアを試してください"
},
"4": {
"q": "HTTPプロキシとは何ですか",
"a": "インターネット上で認識される国名を偽造して、特定の国でのみ閲覧できる情報を取得できるようにすることができます"
},
"5": {
"q": "HTTPプロキシアドレスはどこから入手できますか",
"a": "あなたのPC上で実行する方法を検索する他、Web上でプロキシリストを入手することができます HTTPプロキシは、公式Discordサーバーで提供される予定ですので、ぜひ継続して確認しておいてください"
},
"6": {
"q": "通信が遅いです",
"a": "間違ったアドレスを指定している可能性があります 設定済みのBareサーバーのアドレスを確認して、その接続しているサーバーが近い国である事も確認してください"
}
},
"credits": {
"devs": "開発者一覧",
"jpTranslators": "日本語翻訳",
"esTranslators": "スペイン語翻訳"
}
}

View file

@ -1,4 +0,0 @@
.highlighted {
background-color: var(--navbar-text-color);
transition: background-color 1s ease;
}

View file

@ -1,51 +0,0 @@
import { useTranslation } from "react-i18next";
import { HeaderRoute } from "../components/HeaderRoute";
import CloakedHead from "../util/CloakedHead";
import { useEffect } from "preact/hooks";
import "./Faq.css";
export function Faq() {
const { t } = useTranslation();
const faqData = t("faq", { returnObjects: true });
useEffect(() => {
const hash = window.location.hash.substring(1);
if (hash) {
const highlightedDiv = document.getElementById(hash);
if (highlightedDiv) {
highlightedDiv.classList.add("highlighted");
}
}
}, []);
return (
<HeaderRoute>
<CloakedHead
originalTitle={t("titles.discord")}
originalFavicon="/logo.png"
/>
<div class="p-10 text-input-text">
{Object.values(faqData).map((item, index) => (
<div key={index} className="py-3">
<p className="text-4xl" id={(index + 1).toString()}>
{item.q}
</p>
<div class="flex flex-row">
<p className="text-lg">
{item.a}{" "}
{item.h && (
<a href={item.hR} class="underline">
{item.h}
</a>
)}
</p>
</div>
</div>
))}
</div>
</HeaderRoute>
);
}

View file

@ -1,209 +0,0 @@
import { useState } from "preact/hooks";
import { useTranslation } from "react-i18next";
import { HeaderRoute } from "../components/HeaderRoute";
import { set } from "../util/IDB";
import { uninstallServiceWorkers } from "../util/SWHelper";
import prod from "./config.json"; // Set prod to true if you wish to load balance
import { enc } from "../aes";
import CloakedHead from "../util/CloakedHead";
import { useEffect } from "preact/hooks";
import { updateServiceWorkers } from "../util/SWHelper.js";
import { setTransport } from "../util/transports";
export function Home() {
const [isFocused, setIsFocused] = useState(false);
const [showSuggestions, setShowSuggestions] = useState(false);
const [inputValue, setInputValue] = useState("");
const [suggestions, setSuggestions] = useState([]);
useEffect(() => {
const handleLoad = () => {
const firstLoad = localStorage.getItem("firstLoad") || "true";
console.log(firstLoad);
//make sure service workers are updated
//updateServiceWorkers();
//make sure transport is set
//setTransport();
if (firstLoad == "true" && prod) {
function changeBare(url: string) {
set("bare", url);
localStorage.setItem("bare", url);
uninstallServiceWorkers();
window.location.reload();
}
async function test() {
const nonProtocolOrigin = window.location.origin.replace(
/^https?:\/\//,
""
);
// Do Tokyo-US pinging to find optimal server
const ping = async (url: string) => {
let start = Date.now();
await fetch(url);
let end = Date.now();
let total = end - start;
return total;
};
const usUrl = "https://us." + nonProtocolOrigin;
const jpUrl = "https://jp." + nonProtocolOrigin;
console.log(usUrl, jpUrl);
const [usTime, jpTime] = await Promise.all([
ping(usUrl),
ping(jpUrl)
]);
if (usTime < jpTime) {
console.log("US faster");
changeBare(usUrl);
} else {
console.log("Japan faster");
changeBare(jpUrl);
}
localStorage.setItem("firstLoad", "false");
}
test();
}
};
window.addEventListener("load", handleLoad);
return () => window.removeEventListener("load", handleLoad);
}, []);
const { t } = useTranslation();
const handleInputChange = async (event) => {
setInputValue((event.target as HTMLInputElement).value);
const newQuery = event.target.value;
setInputValue(newQuery);
const response = await fetch(`/search=${newQuery}`).then((res) =>
res.json()
);
const newSuggestions = response?.map((item) => item.phrase) || [];
setSuggestions(newSuggestions);
};
const handleSubmit = (event) => {
event.preventDefault();
setTransport();
window.location.href =
"/go/" +
encodeURIComponent(
//@ts-ignore
enc(
inputValue,
window.location.origin.slice(8) + navigator.userAgent
).substring(10)
);
};
useEffect(() => {
const epoxyScript = document.createElement("script");
epoxyScript.src = "/epoxy/index.js";
epoxyScript.onload = function () {
console.log("lazy loaded epoxy");
};
const libcurlScript = document.createElement("script");
libcurlScript.src = "/libcurl/index.js";
libcurlScript.onload = function () {
console.log("lazy loaded libcurl");
};
document.body.appendChild(epoxyScript);
document.body.appendChild(libcurlScript);
}, []);
return (
<HeaderRoute>
<CloakedHead
originalTitle={t("titles.home")}
originalFavicon="/logo.png"
/>
<div className="flex h-full flex-col items-center justify-center">
<div className="font-inter absolute bottom-0 left-0 p-4 text-sm italic text-input-text">
Nebula &copy; Nebula Services {new Date().getUTCFullYear()}
</div>
<a href="https://github.com/NebulaServices/Nebula">
<div className="font-inter absolute bottom-0 right-0 p-4 text-sm text-input-text">
GitHub
</div>
</a>
<form
onSubmit={handleSubmit}
className="flex h-full w-full flex-col items-center justify-center"
>
<input
onFocus={() => {
setShowSuggestions(true);
setIsFocused(true);
}}
onBlur={() => {
setIsFocused(false);
setTimeout(() => {
setShowSuggestions(false); // delay so the user has time to click suggestions
}, 200);
}}
type="text"
value={inputValue}
onChange={handleInputChange}
className={`font-roboto h-14 rounded-t-2xl border border-input-border-color bg-input p-2 text-center text-xl text-input-text placeholder:text-input-text focus:outline-none ${
isFocused && inputValue.trim() !== ""
? "w-10/12 md:w-3/12"
: "w-80 rounded-2xl"
} transition-all duration-300`}
placeholder={isFocused ? "" : t("home.placeholder")}
/>
<div className="relative flex w-10/12 flex-col items-center md:w-3/12">
<div className="absolute w-full text-center">
{showSuggestions &&
suggestions.map((suggestion, index) => (
<a
onClick={() => {setTransport()}}
href={
"/go/" +
encodeURIComponent(
//@ts-ignore
enc(
suggestion,
window.location.origin.slice(8) + navigator.userAgent
).substring(10)
)
}
>
<div
className={`font-roboto w-110 flex h-10 flex-none shrink-0 items-center justify-center border border-input-border-color bg-input p-2 text-xl text-input-text hover:bg-dropdown-option-hover-color ${
index === suggestions.length - 1 ? "rounded-b-2xl" : ""
}`}
key={index}
>
{suggestion}
</div>
</a>
))}
{/* {showSuggestions &&
Array.from({ length: 10 }, (_, index) => (
<div
key={index}
className="font-roboto w-110 hover:bg-dropdown-option-hover-colo flex h-8 flex-none shrink-0 items-center justify-center border border-input-border-color p-2 text-2xl"
>
Example suggestion
</div>
))}
*/}
</div>
</div>
</form>
</div>
</HeaderRoute>
);
}

View file

@ -1,115 +0,0 @@
import { RammerheadEncode } from "../util/RammerheadEncode";
import { searchUtil } from "../util/searchUtil";
import { LoadSuspense } from "../LoadSuspense";
import { useEffect, useState } from "preact/hooks";
//import our Iframe component
import { Iframe } from "../components/iframe/Iframe";
import CloakedHead from "../util/CloakedHead";
import SiteSupport from "../util/SiteSupport.json";
import { useTranslation } from "react-i18next";
import { dec } from "../aes";
declare global {
interface Window {
__uv$config: any;
__dynamic$config: any;
}
}
export function ProxyFrame(props: { url: string }) {
const { t } = useTranslation();
// pass the URL encoded with encodeURIcomponent
const localProxy = localStorage.getItem("proxy") || "automatic";
const proxyMode = localStorage.getItem("proxyMode") || "embed";
const searchEngine =
localStorage.getItem("searchEngine") || "https://duckduckgo.com/?q=%s";
const [ProxiedUrl, setProxiedUrl] = useState<string | undefined>(undefined);
//@ts-ignore
let decodedUrl = dec(
"U2FsdGVkX1" + decodeURIComponent(props.url),
window.location.origin.slice(8) + navigator.userAgent
);
//attempt to convert to a valid url
decodedUrl = searchUtil(decodedUrl, searchEngine);
let proxyRef;
useEffect(() => {
const fetchData = async () => {
try {
let result: any = "";
if (localProxy === "rammerhead") {
result = await RammerheadEncode(decodedUrl);
} else if (localProxy === "ultraviolet") {
result =
window.__uv$config.prefix +
window.__uv$config.encodeUrl(decodedUrl);
} else if (localProxy === "dynamic") {
result =
window.__dynamic$config.prefix +
"route?url=" +
encodeURIComponent(decodedUrl);
} else {
// automatic. use SiteSupport.json to determine proxy support
const matchingKey = Object.keys(SiteSupport).find((key) =>
decodedUrl.includes(key)
);
if (SiteSupport[matchingKey] === "ultraviolet") {
result =
window.__uv$config.prefix +
window.__uv$config.encodeUrl(decodedUrl);
} else if (SiteSupport[matchingKey] === "dynamic") {
result =
window.__dynamic$config.prefix +
"route?url=" +
encodeURIComponent(decodedUrl);
} else if (SiteSupport[matchingKey] === "rammerhead") {
result = await RammerheadEncode(decodedUrl);
} else {
result =
window.__uv$config.prefix +
window.__uv$config.encodeUrl(decodedUrl); // uv as backup
}
}
setProxiedUrl(result);
} catch (error) {
console.error("Error fetching data:", error);
}
};
fetchData();
}, [localProxy, decodedUrl]);
if (proxyMode == "direct") {
console.log(ProxiedUrl);
console.log(!(ProxiedUrl == undefined));
if (!(ProxiedUrl == undefined)) {
window.location.href = ProxiedUrl; // This is the hackiest workaround in the history of hacky workarounds
}
} else if (proxyMode == "aboutblank") {
if (!(ProxiedUrl == undefined)) {
window.location.href = "/ab/" + encodeURIComponent(ProxiedUrl);
}
}
if (!ProxiedUrl == undefined) {
window.location.href = ProxiedUrl;
}
if (proxyMode === "embed") {
history.pushState({}, "", "/");
}
return (
<div className="h-screen w-screen bg-primary">
<CloakedHead
originalTitle={t("titles.home")}
originalFavicon="/logo.png"
/>
{proxyMode === "direct" && <LoadSuspense />}
{proxyMode === "aboutblank" && <LoadSuspense />}
{proxyMode === "embed" && <Iframe url={ProxiedUrl} />}
</div>
);
}

View file

@ -1,19 +0,0 @@
import { HeaderRoute } from "../components/HeaderRoute";
interface Window {
__uv$config: any;
}
export function Radon() {
//make sure there is a transport set
//setTransport();
return (
<HeaderRoute>
<iframe
src={
window.__uv$config.prefix +
window.__uv$config.encodeUrl("https://radon.games")
}
className="h-full w-full"
></iframe>
</HeaderRoute>
);
}

View file

@ -1,88 +0,0 @@
import { useState, useEffect } from "preact/hooks";
import { set } from "../../util/IDB";
import { uninstallServiceWorkers } from "../../util/SWHelper";
import { useTranslation } from "react-i18next";
import { ToastContainer, toast } from "react-toastify";
import { BareTest } from "./BareTest";
interface BareInputProps {
placeholder: string;
storageKey: string;
}
function BareInput(props: BareInputProps) {
const { t } = useTranslation();
const value = localStorage.getItem(props.storageKey) || "/bare/";
const [inputValue, setInputValue] = useState(value);
function validateUrl(url: string) {
let finalUrl = url;
if (url === "/bare/" || url === "/bare") {
finalUrl = "/bare/";
return finalUrl;
}
if (url === null || url === undefined || url === "") {
finalUrl = "/bare/";
return finalUrl;
}
if (!url.endsWith("/")) {
finalUrl = url + "/";
}
if (!finalUrl.startsWith("http://") && !finalUrl.startsWith("https://")) {
finalUrl = "https://" + finalUrl;
}
return finalUrl;
}
function handleChange() {
const url = validateUrl(
(document.getElementById("input") as HTMLInputElement).value
);
BareTest(url + "v3/").then((result) => {
if (result) {
setInputValue(
(document.getElementById("input") as HTMLInputElement).value
);
set(props.storageKey, url);
set("HTTPProxy", ""); // Disable http proxy servicesssss (most bare servers won't support these and we don't want to be untruthful to the user.)
localStorage.setItem("HTTPProxy", "");
localStorage.setItem(props.storageKey, url);
uninstallServiceWorkers();
window.location.reload();
} else {
(document.getElementById("input") as HTMLInputElement).value =
localStorage.getItem("bare") || "/bare/";
toast(t("bareError"), {
type: "error"
});
}
});
}
return (
<div>
<ToastContainer position="bottom-right" theme="dark" />
<div className="flex flex-col items-center">
<input
type="text"
placeholder={props.placeholder}
value={inputValue}
onKeyPress={(event) => {
if (event.key === "Enter") {
handleChange();
}
}}
id="input"
className="font-roboto flex h-14 w-56 flex-row rounded-2xl border border-input-border-color bg-input p-4 text-center text-xl text-input-text"
/>
<div
className="font-roboto mt-2 flex h-4 w-36 cursor-pointer flex-row items-center justify-center rounded-xl border border-input-border-color bg-input p-5 text-center text-lg text-input-text"
onClick={handleChange}
>
{t("settings.bare.select")}
</div>
</div>
</div>
);
}
export default BareInput;

View file

@ -1,29 +0,0 @@
export function BareTest(bareUrl) {
const headers = new Headers({
"x-bare-url": "https://www.google.com",
"X-Bare-Headers": JSON.stringify({
Accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
})
});
return fetch(bareUrl, {
method: "GET",
headers: headers
})
.then((response) => {
if (
response.headers.get("x-bare-status") === "200" ||
response.headers.get("x-bare-status") === "302"
) {
return true;
} else {
// the site is a real site but doesn't act like a bare server
return false;
}
})
.catch((error) => {
// incase the site doesn't exist
return false;
});
}

View file

@ -1,30 +0,0 @@
import { useState, useEffect } from "preact/hooks";
interface Props {
faviconUrl: string;
title: string;
}
const CloakPreset = (props: Props) => {
const cloak = (event: any) => {
event.preventDefault();
console.log(props.faviconUrl);
localStorage.setItem("cloakFavicon", props.faviconUrl);
localStorage.setItem("cloakTitle", props.title);
window.location.reload();
};
return (
<div
onClick={cloak}
className="flex h-16 w-16 cursor-pointer rounded-full border border-input-border-color bg-lighter"
>
<img
src={props.faviconUrl === "none" ? "/logo.png" : props.faviconUrl}
className="h-16 w-16 p-4"
/>
</div>
);
};
export default CloakPreset;

View file

@ -1,104 +0,0 @@
import { motion } from "framer-motion";
import { tabContentVariant, settingsPageVariant } from "./Variants";
import Dropdown from "./Dropdown";
import { useTranslation } from "react-i18next";
import { PersonCard } from "./PersonCard";
export const Credits = ({ id, active }) => {
const { t } = useTranslation();
return (
<motion.div
role="tabpanel"
id={id}
className="tab-content"
variants={tabContentVariant}
animate={active ? "active" : "inactive"}
initial="inactive"
>
<motion.div
variants={settingsPageVariant}
className="content-card flex w-full flex-row flex-wrap justify-center gap-4"
>
<div className="w-full p-10 text-input-text">
<div className="py-3">
<p className="text-4xl">{t("credits.devs")}</p>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
<PersonCard
name="Riftriot"
url="https://github.com/riftriot/"
profile="https://avatars.githubusercontent.com/u/117926989?v=4"
/>
<PersonCard
name="MotorTruck1221"
url="https://github.com/MotorTruck1221"
profile="https://avatars.githubusercontent.com/u/73721704?v=4"
/>
<PersonCard
name="Cohen"
url="https://github.com/cohenerickson"
profile="https://avatars.githubusercontent.com/u/72945444?v=4"
/>
<PersonCard
name="FireStreaker2"
url="https://github.com/FireStreaker2"
profile="https://avatars.githubusercontent.com/u/103970465?v=4"
/>
<PersonCard
name="wearr"
url="https://github.com/wearrrrr"
profile="https://avatars.githubusercontent.com/u/99224452?v=4"
/>
</div>
<p className="mt-12 text-4xl">{t("credits.jpTranslators")}</p>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
<PersonCard
name="ProgrammerIn-wonderland"
url="https://github.com/ProgrammerIn-wonderland"
profile="https://avatars.githubusercontent.com/u/30693865?v=4"
/>
<PersonCard
name="wearr"
url="https://github.com/wearrrrr"
profile="https://avatars.githubusercontent.com/u/99224452?v=4"
/>
<PersonCard
name="suzumiya39"
url="https://github.com/suzumiya39"
profile="https://avatars.githubusercontent.com/u/165246341?v=4"
/>
</div>
<p className="mt-12 text-4xl">{t("credits.esTranslators")}</p>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
<PersonCard
name="Cohen"
url="https://github.com/cohenerickson"
profile="https://avatars.githubusercontent.com/u/72945444?v=4"
/>
<PersonCard
name="Notplayingallday383"
url="https://github.com/Notplayingallday383"
profile="https://avatars.githubusercontent.com/u/72810050?v=4"
/>
</div>
<a href="https://github.com/titaniumnetwork-dev/Ultraviolet">
<p className="mt-12 text-4xl underline">Ultraviolet</p>
</a>
<a href="https://github.com/binary-person/Rammerhead">
<p className="mt-12 text-4xl underline">Rammerhead</p>
</a>
<a href="https://github.com/nebulaservices/dynamic">
<p className="mt-12 text-4xl underline">Dynamic</p>
</a>
<a href="https://github.com/MercuryWorkshop/epoxy-tls">
<p className="mt-12 text-4xl underline">epoxy-tls</p>
</a>
<a href="https://github.com/MercuryWorkshop/libcurl.js">
<p className="mt-12 text-4xl underline">libcurl.js</p>
</a>
</div>
</div>
</motion.div>
</motion.div>
);
};

View file

@ -1,74 +0,0 @@
import { useRef } from "preact/hooks";
import { useTheme } from "../../components/ThemeProvider";
import { motion } from "framer-motion";
import { tabContentVariant, settingsPageVariant } from "./Variants";
import { useTranslation } from "react-i18next";
import Dropdown from "./Dropdown";
import ThemeDropdown from "./ThemeDropdown";
function Customization({ id, active }) {
const bgInput = useRef<HTMLInputElement>(null);
const { t } = useTranslation();
const { background, setBackground } = useTheme();
const particles = [
{ id: "none", label: t("settings.theme.particlesNone") },
{ id: "/crismas.json", label: t("themes.crismas") }
];
return (
<motion.div
role="tabpanel"
id={id}
className="tab-content"
variants={tabContentVariant}
animate={active ? "active" : "inactive"}
initial="inactive"
>
<motion.div
variants={settingsPageVariant}
className="content-card flex w-full flex-row flex-wrap justify-center gap-4"
>
<div className="flex h-64 w-80 flex-col flex-wrap content-center items-center rounded-lg border border-input-border-color bg-lighter p-2 text-center">
<div className="p-2 text-3xl font-bold text-input-text">
{t("settings.theme.title")}
</div>
<div className="text-md p-4 font-bold text-input-text">
{t("settings.theme.subtitle")}
</div>
<ThemeDropdown />
</div>
<div className="flex h-64 w-80 flex-col flex-wrap content-center items-center rounded-lg border border-input-border-color bg-lighter p-2 text-center">
<div className="p-2 text-3xl font-bold text-input-text">
{t("settings.theme.particles")}
</div>
<div className="text-md p-4 font-bold text-input-text">
{t("settings.theme.particlesDesc")}
</div>
<Dropdown storageKey="particles" options={particles} refresh={true} />
</div>
<div className="flex h-64 w-80 flex-col flex-wrap content-center items-center rounded-lg border border-input-border-color bg-lighter p-2 text-center">
<div className="p-2 text-3xl font-bold text-input-text">
{t("settings.theme.background")}
</div>
<div className="text-md p-4 font-bold text-input-text">
{t("settings.theme.backgroundDesc")}
</div>
<input
ref={bgInput}
className="font-roboto h-14 rounded-2xl border border-input-border-color bg-input p-2 text-center text-xl text-input-text placeholder:text-input-text"
defaultValue={background}
placeholder={t("settings.theme.backgroundNone")}
/>
<button
onClick={() => setBackground(bgInput.current.value)}
className="font-roboto mt-2 flex h-4 w-36 cursor-pointer flex-row items-center justify-center rounded-xl border border-input-border-color bg-input p-5 text-center text-lg text-input-text"
>
{t("settings.theme.select")}
</button>
</div>
</motion.div>
</motion.div>
);
}
export default Customization;

View file

@ -1,87 +0,0 @@
import { useState, useEffect } from "preact/hooks";
import { FaAngleDown } from "react-icons/fa";
interface Option {
id: string;
label: string; // Translations CAN be passed
image?: any;
}
const Dropdown = ({
storageKey,
options,
refresh
}: {
storageKey: string;
options: Option[];
refresh: boolean;
}) => {
const [isOpen, setIsOpen] = useState(false);
const [choice, setChoice] = useState(() => {
return localStorage.getItem(storageKey) || options[0]?.id || "";
});
// update on localstorage change
useEffect(() => {
setChoice(localStorage.getItem(storageKey) || options[0]?.id || "");
}, [storageKey, options]);
return (
<div className="relative text-center">
<div
className={`font-roboto flex h-14 w-56 cursor-pointer flex-col items-center justify-center border border-input-border-color bg-input text-center text-xl ${
isOpen ? "rounded-t-2xl" : "rounded-2xl"
}`}
onClick={() => setIsOpen(!isOpen)}
>
<div className="flex h-full w-full select-none flex-row items-center">
<div className="h-full w-1/4"></div>
<div className="flex w-2/4 flex-row items-center justify-center text-input-text">
{options.find((o) => o.id === choice)?.image && (
<img
src={options.find((o) => o.id === choice)?.image}
className="mr-2 h-6 w-6"
/>
)}
{options.find((o) => o.id === choice)?.label}
</div>
<div className="flex w-1/4 flex-col items-center text-input-text">
<FaAngleDown />
</div>
</div>
{isOpen && (
<div className="absolute top-full w-full">
{options.map((option, index) => (
<div
key={option.id}
className={`flex flex-row justify-center border border-input-border-color bg-input p-2 text-input-text hover:bg-dropdown-option-hover-color ${
index === options.length - 1 ? "rounded-b-2xl" : ""
}`}
onClick={() => {
setIsOpen(false);
setChoice(option.id);
localStorage.setItem(storageKey, option.id);
if (refresh === true) {
window.location.reload();
}
}}
>
{option.image && (
<img
src={option.image}
alt="Option Image"
className="mr-2 h-6 w-6"
/>
)}
{option.label}
</div>
))}
</div>
)}
</div>
</div>
);
};
export default Dropdown;

View file

@ -1,48 +0,0 @@
import { motion } from "framer-motion";
import { tabContentVariant, settingsPageVariant } from "./Variants";
import Dropdown from "./Dropdown";
import { useTranslation } from "react-i18next";
import EnglishFlag from "../../assets/english.png";
import SpanishFlag from "../../assets/spanish.png";
import JapaneseFlag from "../../assets/japanese.png";
const Misc = ({ id, active }) => {
const { t } = useTranslation();
const languages = [
{ id: "en-US", label: "English", image: EnglishFlag },
{ id: "es", label: "Español", image: SpanishFlag },
{ id: "ja", label: "日本語", image: JapaneseFlag }
];
return (
<motion.div
role="tabpanel"
id={id}
className="tab-content"
variants={tabContentVariant}
animate={active ? "active" : "inactive"}
initial="inactive"
>
<motion.div
variants={settingsPageVariant}
className="content-card flex w-full flex-row flex-wrap justify-center gap-4"
>
<div className="flex h-64 w-80 flex-col flex-wrap content-center items-center rounded-lg border border-input-border-color bg-lighter p-7 text-center">
<div className="text-3xl font-bold text-input-text">
{t("settings.languages.title")}
</div>
<div className="text-md font-bold text-input-text">
{t("settings.languages.subtitle")}
</div>
<Dropdown
storageKey="i18nextLng"
options={languages}
refresh={true}
/>
</div>
</motion.div>
</motion.div>
);
};
export default Misc;

View file

@ -1,18 +0,0 @@
interface props {
name: string;
url: string;
profile: string;
}
export const PersonCard = (props) => {
return (
<div class="mr-8 flex flex-row">
<p className="text-lg">
<a href={props.url} class="underline">
<img src={props.profile} className="h-12 w-12 rounded-md" />
{props.name}
</a>
</p>
</div>
);
};

View file

@ -1,151 +0,0 @@
import { motion } from "framer-motion";
import { tabContentVariant, settingsPageVariant } from "./Variants";
import Dropdown from "./Dropdown";
import BareInput from "./BareInput";
import WispInput from "./WispInput";
import ProxyInput from "./ProxyInput";
import { useTranslation } from "react-i18next";
import TransportDropdown from "./transportDropdown";
import RammerheadLogo from "../../assets/rammerhead.png";
import UltravioletLogo from "../../assets/ultraviolet.png";
import AutomaticLogo from "../../assets/automatic.png";
import DynamicLogo from "../../assets/dynamic.png";
import GoogleLogo from "../../assets/google.png";
import BingLogo from "../../assets/bing.png";
import DuckDuckGoLogo from "../../assets/ddg.png";
const Proxy = ({ id, active }) => {
const { t } = useTranslation();
const transport = localStorage.getItem("transport") || "libcurl";
const proccy = localStorage.getItem("proxy") || "automatic";
const engines = [
{
id: "automatic",
label: t("settings.proxy.automatic"),
image: AutomaticLogo
},
{ id: "ultraviolet", label: "Ultraviolet", image: UltravioletLogo },
{ id: "rammerhead", label: "Rammerhead", image: RammerheadLogo },
{ id: "dynamic", label: "Dynamic", image: DynamicLogo }
];
const proxyModes = [
{ id: "embed", label: t("settings.proxymodes.embed") },
{ id: "direct", label: t("settings.proxymodes.direct") },
{ id: "aboutblank", label: t("settings.proxymodes.aboutblank") }
];
const searchEngines = [
{
id: "https://duckduckgo.com/?q=%s",
label: "DuckDuckGo",
image: DuckDuckGoLogo
},
{
id: "https://google.com/search?q=%s",
label: "Google",
image: GoogleLogo
},
{ id: "https://bing.com/search?q=%s", label: "Bing", image: BingLogo }
];
const wispUrl =
(location.protocol === "https:" ? "wss://" : "ws://") +
location.host +
"/wisp/";
const transports = [
{ id: "libcurl", label: "Libcurl" },
{ id: "bare", label: "Bare Server" },
{ id: "epoxy", label: "Epoxy" }
];
return (
<motion.div
role="tabpanel"
id={id}
className="tab-content"
variants={tabContentVariant}
animate={active ? "active" : "inactive"}
initial="inactive"
>
<motion.div
variants={settingsPageVariant}
className="content-card flex w-full flex-row flex-wrap justify-center gap-4"
>
<div className="flex h-64 w-80 flex-col flex-wrap content-center items-center rounded-lg border border-input-border-color bg-lighter p-2 text-center">
<div className="p-2 text-3xl font-bold text-input-text">
{t("settings.proxy.title")}
</div>
<div className="text-md p-4 font-bold text-input-text">
{t("settings.proxy.subtitle")}
</div>
<Dropdown storageKey="proxy" options={engines} refresh={false} />
</div>
<div className="flex h-64 w-80 flex-col flex-wrap content-center items-center rounded-lg border border-input-border-color bg-lighter p-2 text-center">
<div className="p-2 text-3xl font-bold text-input-text">
{t("settings.proxymodes.title")}
</div>
<div className="text-md p-4 font-bold text-input-text">
{t("settings.proxymodes.subtitle")}
</div>
<Dropdown
storageKey="proxyMode"
options={proxyModes}
refresh={false}
/>
</div>
<div className="flex h-64 w-80 flex-col flex-wrap content-center items-center rounded-lg border border-input-border-color bg-lighter p-2 text-center">
<div className="p-2 text-3xl font-bold text-input-text">
{t("settings.search.title")}
</div>
<div className="text-md p-4 font-bold text-input-text">
{t("settings.search.subtitle")}
</div>
<Dropdown
storageKey="searchEngine"
options={searchEngines}
refresh={false}
/>
</div>
{transport === "bare" && (
<div className="flex h-64 w-80 flex-col flex-wrap content-center items-center rounded-lg border border-input-border-color bg-lighter p-2 text-center">
<div className="p-2 text-3xl font-bold text-input-text">
{t("settings.bare.title")}
</div>
<div className="text-md p-4 font-bold text-input-text">
{t("settings.bare.subtitle")}
</div>
<BareInput placeholder="/bare/" storageKey="bare" />
</div>
)}
{transport !== "bare" && (
<div className="flex h-64 w-80 flex-col flex-wrap content-center items-center rounded-lg border border-input-border-color bg-lighter p-2 text-center">
<div className="p-2 text-3xl font-bold text-input-text">
{t("settings.wisp.title")}
</div>
<div className="text-md p-4 font-bold text-input-text">
{t("settings.wisp.subtitle")}
</div>
<WispInput placeholder={wispUrl} />
</div>
)}
<div className="flex h-64 w-80 flex-col flex-wrap content-center items-center rounded-lg border border-input-border-color bg-lighter p-2 text-center">
<div className="p-2 text-3xl font-bold text-input-text">
{t("settings.transport.title")}
</div>
<div className="text-md p-4 font-bold text-input-text">
{t("settings.transport.desc")}
</div>
<TransportDropdown
storageKey="transport"
options={transports}
refresh={false}
/>
</div>
</motion.div>
</motion.div>
);
};
export default Proxy;

View file

@ -1,140 +0,0 @@
import { useState, useEffect } from "preact/hooks";
import { set } from "../../util/IDB";
import { uninstallServiceWorkers } from "../../util/SWHelper";
import { useTranslation } from "react-i18next";
import { ToastContainer, toast } from "react-toastify";
interface BareInputProps {
placeholder: string;
storageKey: string;
}
function ProxyInput(props: BareInputProps) {
const { t } = useTranslation();
const bareServer = localStorage.getItem("bare") || "/bare/";
const HTTPProxy = localStorage.getItem("HTTPProxy") || "";
const [inputValue, setInputValue] = useState(HTTPProxy);
function resetProxy() {
set("HTTPProxy", "");
localStorage.setItem("HTTPProxy", "");
uninstallServiceWorkers();
window.location.reload();
}
function validateUrl(url: string) {
let finalUrl = url;
if (url === null || url === undefined || url === "") {
finalUrl = "";
return finalUrl;
}
return finalUrl;
}
function handleChange() {
const proxyUrl = validateUrl(
(document.getElementById("pinput") as HTMLInputElement).value
);
if (!(proxyUrl === "")) {
const [proxyIP, proxyPort] = proxyUrl.split(":");
fetch(bareServer)
.then((response) => response.json())
.then((jsonResponse) => {
if (jsonResponse.hasOwnProperty("HTTPProxy")) {
const headers = new Headers({
"x-bare-url": "https://www.google.com",
"X-Bare-Headers": JSON.stringify({
Accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
}),
"x-bare-proxy-ip": proxyIP,
"x-bare-proxy-port": proxyPort
});
return fetch(bareServer + "v3/", {
method: "GET",
headers: headers
})
.then((response) => {
if (
response.headers.get("x-bare-status") === "200" ||
response.headers.get("x-bare-status") === "302"
) {
// Success!
set("HTTPProxy", proxyUrl);
localStorage.setItem("HTTPProxy", proxyUrl);
uninstallServiceWorkers();
window.location.reload();
return true;
} else {
(
document.getElementById("pinput") as HTMLInputElement
).value = localStorage.getItem("HTTPProxy") || "";
toast(t("settings.httpProxy.badProxy"), {
type: "error"
});
}
})
.catch((error) => {
(document.getElementById("pinput") as HTMLInputElement).value =
localStorage.getItem("HTTPProxy") || "";
toast(t("settings.httpProxy.badProxy"), {
type: "error"
});
});
} else {
(document.getElementById("pinput") as HTMLInputElement).value =
localStorage.getItem("HTTPProxy") || "";
toast(t("settings.httpProxy.badBare"), {
type: "error"
});
}
})
.catch((error) => console.error("Error:", error));
} else {
// reset UV config to have no proxy
set("HTTPProxy", "");
localStorage.setItem("HTTPProxy", "");
uninstallServiceWorkers();
window.location.reload();
}
}
return (
<div>
<ToastContainer position="bottom-right" theme="dark" />
<div className="flex flex-col items-center">
<input
type="text"
placeholder={props.placeholder}
value={inputValue}
onKeyPress={(event) => {
if (event.key === "Enter") {
handleChange();
}
}}
id="pinput"
className="font-roboto flex h-14 w-56 flex-row rounded-2xl border border-input-border-color bg-input p-4 text-center text-sm text-input-text"
/>
<div class="flex flex-row gap-4">
<div
className="font-roboto mt-2 flex h-4 w-36 cursor-pointer flex-row items-center justify-center rounded-xl border border-input-border-color bg-input p-5 text-center text-lg text-input-text"
onClick={handleChange}
>
{t("settings.bare.select")}
</div>
<div
className="font-roboto mt-2 flex h-4 w-36 cursor-pointer flex-row items-center justify-center rounded-xl border border-input-border-color bg-input p-5 text-center text-lg text-input-text"
onClick={resetProxy}
>
{t("settings.httpProxy.reset")}
</div>
</div>
</div>
</div>
);
}
export default ProxyInput;

View file

@ -1,109 +0,0 @@
import { useState, useEffect } from "preact/hooks";
import cn from "classnames";
import { motion } from "framer-motion";
import "./styles.css";
import { useTranslation } from "react-i18next";
const tabVariant = {
active: {
width: "55%",
transition: {
type: "tween",
duration: 0.4
}
},
inactive: {
width: "15%",
transition: {
type: "tween",
duration: 0.4
}
}
};
const tabTextVariant = {
active: {
opacity: 1,
x: 0,
display: "block",
transition: {
type: "tween",
duration: 0.3,
delay: 0.3
}
},
inactive: {
opacity: 0,
x: -30,
transition: {
type: "tween",
duration: 0.3,
delay: 0.1
},
transitionEnd: { display: "none" }
}
};
const TabComponent = ({ tabs, defaultIndex = 0 }) => {
const [activeTabIndex, setActiveTabIndex] = useState(defaultIndex);
useEffect(() => {
document.documentElement.style.setProperty(
"--active-color",
tabs[activeTabIndex].color
);
}, [activeTabIndex, tabs]);
// Default to a tab based on the URL hash value
useEffect(() => {
const tabFromHash = tabs.findIndex(
(tab) => `#${tab.id}` === window.location.hash
);
setActiveTabIndex(tabFromHash !== -1 ? tabFromHash : defaultIndex);
}, [tabs, defaultIndex]);
const onTabClick = (index) => {
setActiveTabIndex(index);
};
const { t } = useTranslation();
return (
<div className="flex h-full w-full flex-col items-center">
<div className="container h-full w-full">
<div className="tabs-component">
<ul className="tab-links" role="tablist">
{tabs.map((tab, index) => (
<motion.li
key={tab.id}
className={
"flex h-12 flex-row " +
cn("tab", { active: activeTabIndex === index })
}
role="presentation"
variants={tabVariant}
animate={activeTabIndex === index ? "active" : "inactive"}
>
<a href={`#${tab.id}`} onClick={() => onTabClick(index)}>
{tab.icon}
<motion.span variants={tabTextVariant}>
{t(tab.title)}
</motion.span>
</a>
</motion.li>
))}
</ul>
{tabs.map((tab, index) => (
<tab.content
key={tab.id}
id={`${tab.id}-content`}
active={activeTabIndex === index}
/>
))}
</div>
</div>
</div>
);
};
export default TabComponent;

View file

@ -1,86 +0,0 @@
import { motion } from "framer-motion";
import { tabContentVariant, settingsPageVariant } from "./Variants";
import CloakPreset from "./CloakPreset";
import { useTranslation } from "react-i18next";
import { LoadSuspense } from "../../LoadSuspense";
const TabSettings = ({ id, active }) => {
const { t } = useTranslation();
return (
<motion.div
role="tabpanel"
id={id}
className="tab-content"
variants={tabContentVariant}
animate={active ? "active" : "inactive"}
initial="inactive"
>
<motion.div
variants={settingsPageVariant}
className="content-card flex w-full flex-col items-center justify-center text-center"
>
<div className="text-3xl font-bold text-input-text">
{t("settings.cloaking.title")}
</div>
<div className="text-md pb-5 font-bold text-input-text">
{t("settings.cloaking.subtitle")}
</div>
<div className="flex flex-row flex-wrap items-center justify-center gap-4">
<CloakPreset faviconUrl="none" title="none" />
<CloakPreset
faviconUrl="https://t1.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://google.com&size=32"
title="Google"
/>
<CloakPreset
faviconUrl="https://www.wikipedia.org/static/favicon/wikipedia.ico"
title="Wikipedia"
/>
<CloakPreset
faviconUrl="https://du11hjcvx0uqb.cloudfront.net/dist/images/favicon-e10d657a73.ico"
title="Dashboard"
/>
<CloakPreset
faviconUrl="https://ssl.gstatic.com/classroom/ic_product_classroom_144.png"
title="Home"
/>
<CloakPreset
faviconUrl="https://asset-cdn.schoology.com/sites/all/themes/schoology_theme/favicon.ico"
title="Schoology"
/>
</div>
<div className="relative p-4">
<button
className="font-roboto h-14 w-56 rounded-2xl border border-input-border-color bg-input p-2 text-center text-xl font-bold text-input-text placeholder:text-input-text focus:outline-none"
onClick={() => {
let newWindow = window.open("about:blank");
let iframe = document.createElement("iframe");
iframe.src = window.location.origin;
iframe.style.width = "100%";
iframe.style.height = "100%";
iframe.style.border = "none";
iframe.style.overflow = "hidden";
iframe.style.margin = "0";
iframe.style.padding = "0";
iframe.style.position = "fixed";
iframe.style.top = "0";
iframe.style.bottom = "0";
iframe.style.left = "0";
iframe.style.right = "0";
newWindow.document.body.appendChild(iframe);
window.location.replace("https://google.com");
return (
<div>
<LoadSuspense />
</div>
);
}}
>
{t("settings.cloaking.aboutblank")}
</button>
</div>
</motion.div>
</motion.div>
);
};
export default TabSettings;

View file

@ -1,53 +0,0 @@
import { useState } from "preact/hooks";
import { FaAngleDown } from "react-icons/fa";
import { useTheme } from "../../components/ThemeProvider";
import { useTranslation } from "react-i18next";
const ThemeDropdown = () => {
const [isOpen, setIsOpen] = useState(false);
const { t } = useTranslation();
const { theme, setTheme, themes } = useTheme();
const options = themes.map((theme) => {
return { id: theme, label: t(`themes.${theme}`) };
});
return (
<div className="relative text-center">
<div
className={`font-roboto flex h-14 w-56 cursor-pointer flex-col items-center justify-center border border-input-border-color bg-input text-center text-xl ${
isOpen ? "rounded-t-2xl" : "rounded-2xl"
}`}
onClick={() => setIsOpen(!isOpen)}
>
<div className="flex h-full w-full select-none flex-row items-center">
<div className="h-full w-1/4"></div>
<div className="flex w-2/4 flex-row items-center justify-center text-input-text">
{options.find((o) => o.id === theme)?.label}
</div>
<div className="flex w-1/4 flex-col items-center text-input-text">
<FaAngleDown />
</div>
</div>
{isOpen && (
<div className="absolute top-full w-full">
{options.map((option, index) => (
<div
key={option.id}
className={`flex flex-row justify-center border border-input-border-color bg-input p-2 text-input-text hover:bg-dropdown-option-hover-color ${
index === options.length - 1 ? "rounded-b-2xl" : ""
}`}
onClick={() => {
setIsOpen(false);
setTheme(option.id);
}}
>
{option.label}
</div>
))}
</div>
)}
</div>
</div>
);
};
export default ThemeDropdown;

View file

@ -1,30 +0,0 @@
const tabContentVariant = {
active: {
display: "block",
transition: {
staggerChildren: 0.2
}
},
inactive: {
display: "none"
}
};
const settingsPageVariant = {
active: {
opacity: 1,
y: 0,
transition: {
duration: 0.5
}
},
inactive: {
opacity: 0,
y: 10,
transition: {
duration: 0.5
}
}
};
export { settingsPageVariant, tabContentVariant };

View file

@ -1,66 +0,0 @@
import { useState, useEffect } from "preact/hooks";
import { useTranslation } from "react-i18next";
import { changeTransport } from "../../util/transports";
import { ToastContainer, toast } from "react-toastify";
interface WispInputProps {
placeholder: string;
}
function WispInput(props: WispInputProps) {
const { t } = useTranslation();
const value =
localStorage.getItem("wispUrl") ||
(location.protocol === "https:" ? "wss://" : "ws://") +
location.host +
"/wisp/";
const [inputValue, setInputValue] = useState(value);
function validateUrl(url: string) {
let finalUrl = url;
if (finalUrl.startsWith("http://")) {
finalUrl = finalUrl.replace("http://", "ws://");
} else if (finalUrl.startsWith("https://")) {
finalUrl = finalUrl.replace("https://", "wss://");
} else if (finalUrl === "" || finalUrl === null || finalUrl === undefined) {
finalUrl =
(location.protocol === "https:" ? "wss://" : "ws://") +
location.host +
"/wisp/";
}
return finalUrl;
}
function handleChange() {
const url = validateUrl(
(document.getElementById("wispinput") as HTMLInputElement).value
);
localStorage.setItem("wispUrl", url);
changeTransport(localStorage.getItem("transport") || "epoxy", url);
}
return (
<div>
<ToastContainer position="bottom-right" theme="dark" />
<div className="flex flex-col items-center">
<input
type="text"
placeholder={props.placeholder}
value={inputValue}
onKeyPress={(event) => {
if (event.key === "Enter") {
handleChange();
}
}}
id="wispinput"
className="font-roboto flex h-14 w-56 flex-row rounded-2xl border border-input-border-color bg-input p-4 text-center text-xl text-input-text"
/>
<div
className="font-roboto mt-2 flex h-4 w-36 cursor-pointer flex-row items-center justify-center rounded-xl border border-input-border-color bg-input p-5 text-center text-lg text-input-text"
onClick={handleChange}
>
{t("settings.wisp.select")}
</div>
</div>
</div>
);
}
export default WispInput;

View file

@ -1,19 +0,0 @@
import TabComponent from "./TabComponent";
import { HeaderRoute } from "../../components/HeaderRoute";
import tabs from "./tabs";
import { useTranslation } from "react-i18next";
import CloakedHead from "../../util/CloakedHead";
export function Settings() {
const { t } = useTranslation();
return (
<HeaderRoute>
<CloakedHead
originalTitle={t("titles.settings")}
originalFavicon="/logo.png"
/>
<TabComponent tabs={tabs} />
</HeaderRoute>
);
}

View file

@ -1,129 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@100&display=swap");
* {
box-sizing: border-box;
}
:root {
--white: #fff;
--black: #333;
--active-color: #f1f1f1;
--border-radius: 40px;
}
body {
-webkit-font-smoothing: antialiased;
font-family: Roboto;
background: var(--active-color);
transition: background 1.5s ease;
}
img {
max-width: 100%;
vertical-align: middle;
}
.tabs-component {
width: 100%;
height: 100%;
margin: auto;
padding: 40px;
border-radius: var(--border-radius);
}
.tab-links {
padding: 0;
margin: 0 auto 20px;
list-style: none;
max-width: 400px;
display: flex;
justify-content: space-between;
}
.tab {
position: relative;
}
.tab a {
text-decoration: none;
color: var(--black);
}
.tab::before {
content: "";
width: 100%;
height: 100%;
opacity: 0.2;
position: absolute;
border-radius: var(--border-radius);
background: none;
transition: background 0.5s ease;
}
.tab svg {
height: 30px;
width: 30px;
min-width: 30px;
fill: var(--tab-color);
transition: fill 0.5s ease;
}
.tab.active::before {
background: var(--active-color);
}
.tab span {
font-weight: 700;
margin-left: 10px;
transition: color 0.5s ease;
}
.tab.active span {
color: var(--active-color);
}
.tab.active svg {
fill: var(--active-color);
}
.tab a {
padding: 16px;
display: flex;
align-items: center;
font-size: 14px;
overflow: hidden;
position: relative;
}
.cards {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin-top: 40px;
}
.content-card {
width: 48%;
margin-bottom: 26px;
}
.content-card .info::after {
content: "";
display: block;
width: 100%;
height: 3px;
bottom: -5px;
background: var(--active-color);
opacity: 0.5;
}
.content-card img {
border-radius: 6px;
}
.content-card h3 {
margin: 0 0 5px;
}
.content-card .info {
padding: 10px 0;
}

View file

@ -1,51 +0,0 @@
import Proxy from "./Proxy";
import TabSettings from "./TabSettings";
import Misc from "./Misc";
import Customization from "./Customization";
import { GoBrowser } from "react-icons/go";
import { AiOutlineLaptop } from "react-icons/ai";
import { FaPalette } from "react-icons/fa";
import { FaGear } from "react-icons/fa6";
import { IoMdPerson } from "react-icons/io";
import { Credits } from "./Credits";
const tabs = [
{
title: "settings.tabs.proxy",
id: "proxy",
icon: <AiOutlineLaptop />,
color: "#5d5dff",
content: Proxy
},
{
title: "settings.tabs.tab",
id: "tab",
icon: <GoBrowser />,
color: "#67bb67",
content: TabSettings
},
{
title: "settings.tabs.custom",
id: "custom",
icon: <FaPalette />,
color: "#63a7c7",
content: Customization
},
{
title: "settings.tabs.misc",
id: "misc",
icon: <FaGear />,
color: "#f56868",
content: Misc
},
{
title: "settings.tabs.credits",
id: "credits",
icon: <IoMdPerson />,
color: "#fefefe",
content: Credits
}
];
export default tabs;

Some files were not shown because too many files have changed in this diff Show more