mirror of
https://github.com/NebulaServices/Nebula.git
synced 2025-05-13 12:00:01 -04:00
Prettier Format
This commit is contained in:
parent
3a9024d1e0
commit
ad67368a45
42 changed files with 1650 additions and 1609 deletions
8
.prettierignore
Normal file
8
.prettierignore
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Build artifacts
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Other things that don't need to be reformatted
|
||||||
|
public/uv/
|
||||||
|
public/dynamic/
|
||||||
|
pnpm-lock.yaml
|
||||||
|
package-lock.json
|
16
.prettierrc
16
.prettierrc
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"singleQuote": false,
|
"singleQuote": false,
|
||||||
"endOfLine": "crlf",
|
"endOfLine": "crlf",
|
||||||
"tabWidth": 2,
|
"tabWidth": 2,
|
||||||
"useTabs": false,
|
"useTabs": false,
|
||||||
"trailingComma": "none",
|
"trailingComma": "none",
|
||||||
"plugins": ["prettier-plugin-tailwindcss"]
|
"plugins": ["prettier-plugin-tailwindcss"]
|
||||||
}
|
}
|
||||||
|
|
24
.vscode/settings.json
vendored
24
.vscode/settings.json
vendored
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"editor.tabSize": 2,
|
"editor.tabSize": 2,
|
||||||
"editor.insertSpaces": true,
|
"editor.insertSpaces": true,
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.formatOnPaste": true,
|
"editor.formatOnPaste": true,
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"files.autoSave": "afterDelay",
|
"files.autoSave": "afterDelay",
|
||||||
"files.autoSaveDelay": 0,
|
"files.autoSaveDelay": 0,
|
||||||
"css.lint.unknownAtRules": "ignore",
|
"css.lint.unknownAtRules": "ignore",
|
||||||
"editor.linkedEditing": true,
|
"editor.linkedEditing": true,
|
||||||
"css.lint.unknownProperties": "ignore"
|
"css.lint.unknownProperties": "ignore"
|
||||||
}
|
}
|
||||||
|
|
15
README.md
15
README.md
|
@ -1,7 +1,8 @@
|
||||||
# Nebula rewrite
|
# Nebula rewrite
|
||||||
`npm i -g pnpm tsx`
|
|
||||||
`git clone https://github.com/NebulaServices/Nebula.git`
|
`npm i -g pnpm tsx`
|
||||||
`git switch rewrite`
|
`git clone https://github.com/NebulaServices/Nebula.git`
|
||||||
`pnpm i`
|
`git switch rewrite`
|
||||||
|
`pnpm i`
|
||||||
readme meant for devs, will be changed before being merged into main
|
|
||||||
|
readme meant for devs, will be changed before being merged into main
|
||||||
|
|
14
framer-motion.d.ts
vendored
14
framer-motion.d.ts
vendored
|
@ -1,7 +1,7 @@
|
||||||
import * as React from "preact/compat";
|
import * as React from "preact/compat";
|
||||||
|
|
||||||
declare module "framer-motion" {
|
declare module "framer-motion" {
|
||||||
export interface AnimatePresenceProps {
|
export interface AnimatePresenceProps {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
58
index.html
58
index.html
|
@ -1,29 +1,29 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/logo.png" />
|
<link rel="icon" type="image/svg+xml" href="/logo.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="color-scheme" content="light dark" />
|
<meta name="color-scheme" content="light dark" />
|
||||||
<title>Nebula</title>
|
<title>Nebula</title>
|
||||||
<script src="/uv/uv.bundle.js"></script>
|
<script src="/uv/uv.bundle.js"></script>
|
||||||
<script src="/uv/uv.config.js"></script>
|
<script src="/uv/uv.config.js"></script>
|
||||||
<script src="/dynamic/dynamic.config.js"></script>
|
<script src="/dynamic/dynamic.config.js"></script>
|
||||||
<script>
|
<script>
|
||||||
if ("serviceWorker" in navigator) {
|
if ("serviceWorker" in navigator) {
|
||||||
window.addEventListener("load", () => {
|
window.addEventListener("load", () => {
|
||||||
navigator.serviceWorker.register("/uvsw.js", {
|
navigator.serviceWorker.register("/uvsw.js", {
|
||||||
scope: __uv$config.prefix
|
scope: __uv$config.prefix
|
||||||
});
|
});
|
||||||
navigator.serviceWorker.register("/dysw.js", {
|
navigator.serviceWorker.register("/dysw.js", {
|
||||||
scope: __dynamic$config.prefix
|
scope: __dynamic$config.prefix
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/index.tsx"></script>
|
<script type="module" src="/src/index.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
92
package.json
92
package.json
|
@ -1,46 +1,46 @@
|
||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "concurrently \"vite\" \"bare-server-node --port 8080\"",
|
"dev": "concurrently \"vite\" \"bare-server-node --port 8080\"",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"bstart": "npm run build && tsx server.ts",
|
"bstart": "npm run build && tsx server.ts",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"format": "prettier --write ."
|
"format": "prettier --write ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fastify/compress": "^6.5.0",
|
"@fastify/compress": "^6.5.0",
|
||||||
"@fastify/static": "^6.12.0",
|
"@fastify/static": "^6.12.0",
|
||||||
"@titaniumnetwork-dev/ultraviolet": "^2.0.0",
|
"@titaniumnetwork-dev/ultraviolet": "^2.0.0",
|
||||||
"@tomphttp/bare-server-node": "^2.0.1",
|
"@tomphttp/bare-server-node": "^2.0.1",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
"fastify": "^4.25.1",
|
"fastify": "^4.25.1",
|
||||||
"framer-motion": "^10.16.16",
|
"framer-motion": "^10.16.16",
|
||||||
"i18next": "^23.7.9",
|
"i18next": "^23.7.9",
|
||||||
"i18next-browser-languagedetector": "^7.2.0",
|
"i18next-browser-languagedetector": "^7.2.0",
|
||||||
"million": "^2.6.4",
|
"million": "^2.6.4",
|
||||||
"preact": "^10.13.1",
|
"preact": "^10.13.1",
|
||||||
"preact-iso": "^2.3.2",
|
"preact-iso": "^2.3.2",
|
||||||
"preact-render-to-string": "^6.3.1",
|
"preact-render-to-string": "^6.3.1",
|
||||||
"preact-router": "^4.1.2",
|
"preact-router": "^4.1.2",
|
||||||
"rammerhead": "https://github.com/holy-unblocker/rammerhead/releases/download/v1.2.41-holy.5/rammerhead-1.2.41-holy.5.tgz",
|
"rammerhead": "https://github.com/holy-unblocker/rammerhead/releases/download/v1.2.41-holy.5/rammerhead-1.2.41-holy.5.tgz",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-i18next": "^13.5.0",
|
"react-i18next": "^13.5.0",
|
||||||
"react-icons": "^4.12.0",
|
"react-icons": "^4.12.0",
|
||||||
"tsx": "^4.7.0"
|
"tsx": "^4.7.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@preact/preset-vite": "^2.5.0",
|
"@preact/preset-vite": "^2.5.0",
|
||||||
"autoprefixer": "^10.4.16",
|
"autoprefixer": "^10.4.16",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
"eslint": "^8.55.0",
|
"eslint": "^8.55.0",
|
||||||
"eslint-config-preact": "^1.3.0",
|
"eslint-config-preact": "^1.3.0",
|
||||||
"postcss": "^8.4.32",
|
"postcss": "^8.4.32",
|
||||||
"prettier": "^3.1.1",
|
"prettier": "^3.1.1",
|
||||||
"prettier-plugin-tailwindcss": "^0.5.9",
|
"prettier-plugin-tailwindcss": "^0.5.9",
|
||||||
"tailwindcss": "^3.3.6",
|
"tailwindcss": "^3.3.6",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"vite": "^5.0.9",
|
"vite": "^5.0.9",
|
||||||
"vite-plugin-static-copy": "^1.0.0"
|
"vite-plugin-static-copy": "^1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
export default {
|
export default {
|
||||||
plugins: {
|
plugins: {
|
||||||
tailwindcss: {},
|
tailwindcss: {},
|
||||||
autoprefixer: {}
|
autoprefixer: {}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
importScripts("/dynamic/dynamic.config.js");
|
importScripts("/dynamic/dynamic.config.js");
|
||||||
importScripts("/dynamic/dynamic.worker.js");
|
importScripts("/dynamic/dynamic.worker.js");
|
||||||
|
|
||||||
const dynamic = new Dynamic();
|
const dynamic = new Dynamic();
|
||||||
|
|
||||||
self.dynamic = dynamic;
|
self.dynamic = dynamic;
|
||||||
|
|
||||||
self.addEventListener("fetch", (event) => {
|
self.addEventListener("fetch", (event) => {
|
||||||
if (
|
if (
|
||||||
event.request.url.startsWith(location.origin + self.__dynamic$config.prefix)
|
event.request.url.startsWith(location.origin + self.__dynamic$config.prefix)
|
||||||
)
|
)
|
||||||
event.respondWith(
|
event.respondWith(
|
||||||
(async function () {
|
(async function () {
|
||||||
if (await dynamic.route(event)) {
|
if (await dynamic.route(event)) {
|
||||||
return await dynamic.fetch(event);
|
return await dynamic.fetch(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await fetch(event.request);
|
return await fetch(event.request);
|
||||||
})()
|
})()
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
importScripts("/uv/uv.bundle.js");
|
importScripts("/uv/uv.bundle.js");
|
||||||
importScripts("/uv/uv.config.js");
|
importScripts("/uv/uv.config.js");
|
||||||
importScripts(__uv$config.sw || "/uv/uv.sw.js");
|
importScripts(__uv$config.sw || "/uv/uv.sw.js");
|
||||||
|
|
||||||
const sw = new UVServiceWorker();
|
const sw = new UVServiceWorker();
|
||||||
|
|
||||||
self.addEventListener("fetch", (event) => {
|
self.addEventListener("fetch", (event) => {
|
||||||
if (event.request.url.startsWith(location.origin + __uv$config.prefix))
|
if (event.request.url.startsWith(location.origin + __uv$config.prefix))
|
||||||
return event.respondWith(sw.fetch(event));
|
return event.respondWith(sw.fetch(event));
|
||||||
});
|
});
|
||||||
|
|
172
server.ts
172
server.ts
|
@ -1,86 +1,86 @@
|
||||||
import fastify from 'fastify';
|
import fastify from "fastify";
|
||||||
import fastifyStatic from '@fastify/static';
|
import fastifyStatic from "@fastify/static";
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from "url";
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import createRammerhead from "rammerhead/src/server/index.js";
|
import createRammerhead from "rammerhead/src/server/index.js";
|
||||||
import { createBareServer } from "@tomphttp/bare-server-node";
|
import { createBareServer } from "@tomphttp/bare-server-node";
|
||||||
import { createServer } from "http";
|
import { createServer } from "http";
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
const bare = createBareServer("/bare/");
|
const bare = createBareServer("/bare/");
|
||||||
const rh = createRammerhead();
|
const rh = createRammerhead();
|
||||||
|
|
||||||
const rammerheadScopes = [
|
const rammerheadScopes = [
|
||||||
"/rammerhead.js",
|
"/rammerhead.js",
|
||||||
"/hammerhead.js",
|
"/hammerhead.js",
|
||||||
"/transport-worker.js",
|
"/transport-worker.js",
|
||||||
"/task.js",
|
"/task.js",
|
||||||
"/iframe-task.js",
|
"/iframe-task.js",
|
||||||
"/worker-hammerhead.js",
|
"/worker-hammerhead.js",
|
||||||
"/messaging",
|
"/messaging",
|
||||||
"/sessionexists",
|
"/sessionexists",
|
||||||
"/deletesession",
|
"/deletesession",
|
||||||
"/newsession",
|
"/newsession",
|
||||||
"/editsession",
|
"/editsession",
|
||||||
"/needpassword",
|
"/needpassword",
|
||||||
"/syncLocalStorage",
|
"/syncLocalStorage",
|
||||||
"/api/shuffleDict",
|
"/api/shuffleDict",
|
||||||
"/mainport"
|
"/mainport"
|
||||||
];
|
];
|
||||||
|
|
||||||
const rammerheadSession = /^\/[a-z0-9]{32}/;
|
const rammerheadSession = /^\/[a-z0-9]{32}/;
|
||||||
|
|
||||||
function shouldRouteRh(req) {
|
function shouldRouteRh(req) {
|
||||||
const url = new URL(req.url, "http://0.0.0.0");
|
const url = new URL(req.url, "http://0.0.0.0");
|
||||||
return (
|
return (
|
||||||
rammerheadScopes.includes(url.pathname) ||
|
rammerheadScopes.includes(url.pathname) ||
|
||||||
rammerheadSession.test(url.pathname)
|
rammerheadSession.test(url.pathname)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function routeRhRequest(req, res) {
|
function routeRhRequest(req, res) {
|
||||||
rh.emit("request", req, res);
|
rh.emit("request", req, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
function routeRhUpgrade(req, socket, head) {
|
function routeRhUpgrade(req, socket, head) {
|
||||||
rh.emit("upgrade", req, socket, head);
|
rh.emit("upgrade", req, socket, head);
|
||||||
}
|
}
|
||||||
|
|
||||||
const serverFactory = (handler, opts) => {
|
const serverFactory = (handler, opts) => {
|
||||||
return createServer()
|
return createServer()
|
||||||
.on("request", (req, res) => {
|
.on("request", (req, res) => {
|
||||||
if (bare.shouldRoute(req)) {
|
if (bare.shouldRoute(req)) {
|
||||||
bare.routeRequest(req, res);
|
bare.routeRequest(req, res);
|
||||||
} else if (shouldRouteRh(req)) {
|
} else if (shouldRouteRh(req)) {
|
||||||
routeRhRequest(req, res);
|
routeRhRequest(req, res);
|
||||||
} else {
|
} else {
|
||||||
handler(req, res);
|
handler(req, res);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on("upgrade", (req, socket, head) => {
|
.on("upgrade", (req, socket, head) => {
|
||||||
if (bare.shouldRoute(req)) {
|
if (bare.shouldRoute(req)) {
|
||||||
bare.routeUpgrade(req, socket, head);
|
bare.routeUpgrade(req, socket, head);
|
||||||
} else if (shouldRouteRh(req)) {
|
} else if (shouldRouteRh(req)) {
|
||||||
routeRhUpgrade(req, socket, head);
|
routeRhUpgrade(req, socket, head);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const app = fastify({ logger: true, serverFactory });
|
const app = fastify({ logger: true, serverFactory });
|
||||||
|
|
||||||
app.register(fastifyStatic, {
|
app.register(fastifyStatic, {
|
||||||
root: path.join(__dirname, 'dist'),
|
root: path.join(__dirname, "dist"),
|
||||||
prefix: '/',
|
prefix: "/",
|
||||||
serve: true,
|
serve: true,
|
||||||
wildcard: false,
|
wildcard: false
|
||||||
});
|
});
|
||||||
|
|
||||||
app.setNotFoundHandler((req, res) => {
|
app.setNotFoundHandler((req, res) => {
|
||||||
res.sendFile('index.html') // SPA catch-all
|
res.sendFile("index.html"); // SPA catch-all
|
||||||
})
|
});
|
||||||
|
|
||||||
app.listen({
|
app.listen({
|
||||||
port: 8080
|
port: 8080
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { Header } from "./Header";
|
import { Header } from "./Header";
|
||||||
|
|
||||||
export function HeaderRoute(props: { children: any }) {
|
export function HeaderRoute(props: { children: any }) {
|
||||||
return (
|
return (
|
||||||
<div class="flex h-screen flex-col">
|
<div class="flex h-screen flex-col">
|
||||||
<Header />
|
<Header />
|
||||||
<div class="flex-1 bg-primary">
|
<div class="flex-1 bg-primary">
|
||||||
<main class="h-full">{props.children}</main>
|
<main class="h-full">{props.children}</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,23 @@
|
||||||
import { motion } from "framer-motion"
|
import { motion } from "framer-motion";
|
||||||
import { IframeHeader } from "./IframeHeader"
|
import { IframeHeader } from "./IframeHeader";
|
||||||
|
|
||||||
export function Iframe(props: { url: string }) {
|
export function Iframe(props: { url: string }) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<IframeHeader url={ props.url } />
|
<IframeHeader url={props.url} />
|
||||||
<motion.div className="w-full h-[calc(100%_-_4rem)] bg-primary" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.5 }} >
|
<motion.div
|
||||||
<iframe id="iframe" src={ props.url } className="w-full h-full border-none bg-primary" />
|
className="h-[calc(100%_-_4rem)] w-full bg-primary"
|
||||||
</motion.div>
|
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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,33 +1,48 @@
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link } from "preact-router";
|
import { Link } from "preact-router";
|
||||||
import { RiPictureInPictureExitFill, RiFullscreenFill } from "react-icons/ri"
|
import { RiPictureInPictureExitFill, RiFullscreenFill } from "react-icons/ri";
|
||||||
|
|
||||||
export function IframeHeader(props: { url: string }) {
|
export function IframeHeader(props: { url: string }) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation();
|
||||||
const [showPopout, setShowPopout] = useState(false);
|
const [showPopout, setShowPopout] = useState(false);
|
||||||
const [showFullScreen, setFullScreen] = useState(false);
|
const [showFullScreen, setFullScreen] = useState(false);
|
||||||
if (showPopout) {
|
if (showPopout) {
|
||||||
window.location.replace(props.url);
|
window.location.replace(props.url);
|
||||||
}
|
}
|
||||||
if (showFullScreen) {
|
if (showFullScreen) {
|
||||||
document.getElementById("iframe").requestFullscreen();
|
document.getElementById("iframe").requestFullscreen();
|
||||||
setFullScreen(false);
|
setFullScreen(false);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div id="iframeNav" className="flex h-16 flex-row items-center justify-between bg-navbar-color px-4">
|
<div
|
||||||
<Link href="/" class="w-1/2">
|
id="iframeNav"
|
||||||
<div className="flex flex-row items-center">
|
className="flex h-16 flex-row items-center justify-between bg-navbar-color px-4"
|
||||||
<img src="/logo.png" className="h-16 w-16 transition-all duration-1000 hover:rotate-[360deg]"></img>
|
>
|
||||||
<h1 className="font-roboto text-2xl font-bold text-navbar-text-color md:text-4xl"> {t("header.title")} </h1>
|
<Link href="/" class="w-1/2">
|
||||||
</div>
|
<div className="flex flex-row items-center">
|
||||||
</Link>
|
<img
|
||||||
<div id="navItems" class="w-1/2">
|
src="/logo.png"
|
||||||
<div className="flex flex-row items-center justify-end gap-3 mr-4">
|
className="h-16 w-16 transition-all duration-1000 hover:rotate-[360deg]"
|
||||||
<RiPictureInPictureExitFill className="h-6 w-6 cursor-pointer transition-all duration-0500 text-navbar-text-color hover:scale-110 hover:brightness-125" onClick={() => setShowPopout(true)} />
|
></img>
|
||||||
<RiFullscreenFill className="h-6 w-6 cursor-pointer transition-all duration-0500 text-navbar-text-color hover:scale-110 hover:brightness-125 active:rotate-90" onClick={() => setFullScreen(true)} />
|
<h1 className="font-roboto text-2xl font-bold text-navbar-text-color md:text-4xl">
|
||||||
</div>
|
{" "}
|
||||||
</div>
|
{t("header.title")}{" "}
|
||||||
</div>
|
</h1>
|
||||||
)
|
</div>
|
||||||
};
|
</Link>
|
||||||
|
<div id="navItems" class="w-1/2">
|
||||||
|
<div className="mr-4 flex flex-row items-center justify-end gap-3">
|
||||||
|
<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)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
64
src/i18n.ts
64
src/i18n.ts
|
@ -1,32 +1,32 @@
|
||||||
import i18n from "i18next";
|
import i18n from "i18next";
|
||||||
import { initReactI18next } from "react-i18next";
|
import { initReactI18next } from "react-i18next";
|
||||||
import LanguageDetector from "i18next-browser-languagedetector";
|
import LanguageDetector from "i18next-browser-languagedetector";
|
||||||
|
|
||||||
import translationEN from "./locales/en.json";
|
import translationEN from "./locales/en.json";
|
||||||
import translationES from "./locales/es.json";
|
import translationES from "./locales/es.json";
|
||||||
import translationJA from "./locales/ja.json";
|
import translationJA from "./locales/ja.json";
|
||||||
|
|
||||||
const resources = {
|
const resources = {
|
||||||
en: {
|
en: {
|
||||||
translation: translationEN
|
translation: translationEN
|
||||||
},
|
},
|
||||||
es: {
|
es: {
|
||||||
translation: translationES
|
translation: translationES
|
||||||
},
|
},
|
||||||
ja: {
|
ja: {
|
||||||
translation: translationJA
|
translation: translationJA
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
i18n
|
i18n
|
||||||
.use(initReactI18next)
|
.use(initReactI18next)
|
||||||
.use(LanguageDetector)
|
.use(LanguageDetector)
|
||||||
.init({
|
.init({
|
||||||
resources,
|
resources,
|
||||||
fallbackLng: "en",
|
fallbackLng: "en",
|
||||||
interpolation: {
|
interpolation: {
|
||||||
escapeValue: false
|
escapeValue: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default i18n;
|
export default i18n;
|
||||||
|
|
|
@ -1,28 +1,27 @@
|
||||||
import { render } from "preact";
|
import { render } from "preact";
|
||||||
import { LocationProvider, Router, Route } from "preact-iso";
|
import { LocationProvider, Router, Route } from "preact-iso";
|
||||||
import { Home } from "./pages/Home";
|
import { Home } from "./pages/Home";
|
||||||
import { NotFound } from "./pages/_404.jsx";
|
import { NotFound } from "./pages/_404.jsx";
|
||||||
import { DiscordPage } from "./pages/discord.jsx";
|
import { DiscordPage } from "./pages/discord.jsx";
|
||||||
import { ProxyFrame } from "./pages/ProxyFrame.js";
|
import { ProxyFrame } from "./pages/ProxyFrame.js";
|
||||||
import { Settings } from "./pages/Settings/index.js";
|
import { Settings } from "./pages/Settings/index.js";
|
||||||
|
|
||||||
import "./style.css";
|
import "./style.css";
|
||||||
import "./themes/main.css";
|
import "./themes/main.css";
|
||||||
import "./i18n";
|
import "./i18n";
|
||||||
|
|
||||||
export function App() {
|
export function App() {
|
||||||
return (
|
return (
|
||||||
<LocationProvider>
|
<LocationProvider>
|
||||||
<Router>
|
<Router>
|
||||||
<Route path="/" component={Home} />
|
<Route path="/" component={Home} />
|
||||||
<Route path="/discord" component={DiscordPage} />
|
<Route path="/discord" component={DiscordPage} />
|
||||||
<Route path="/go/:url" component={ProxyFrame} />
|
<Route path="/go/:url" component={ProxyFrame} />
|
||||||
<Route path="/settings" component={Settings} />
|
<Route path="/settings" component={Settings} />
|
||||||
<Route default component={NotFound} />
|
<Route default component={NotFound} />
|
||||||
</Router>
|
</Router>
|
||||||
</LocationProvider>
|
</LocationProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render(<App />, document.getElementById("app"));
|
render(<App />, document.getElementById("app"));
|
||||||
|
|
||||||
|
|
|
@ -1,52 +1,52 @@
|
||||||
{
|
{
|
||||||
"header": {
|
"header": {
|
||||||
"title": "Nebel",
|
"title": "Nebel",
|
||||||
"games": "Spiele",
|
"games": "Spiele",
|
||||||
"settings": "einstellungen",
|
"settings": "einstellungen",
|
||||||
"discord": "Sie wollen mehr URL-Adresse?"
|
"discord": "Sie wollen mehr URL-Adresse?"
|
||||||
},
|
},
|
||||||
"404": {
|
"404": {
|
||||||
"text": "Dieser Nebel-Dienst wurde deaktiviert.",
|
"text": "Dieser Nebel-Dienst wurde deaktiviert.",
|
||||||
"return": "Zurück nach Hause."
|
"return": "Zurück nach Hause."
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"placeholder": "Suchen Sie frei im Web."
|
"placeholder": "Suchen Sie frei im Web."
|
||||||
},
|
},
|
||||||
"discord": {
|
"discord": {
|
||||||
"title": "Nebel's Discord Server",
|
"title": "Nebel's Discord Server",
|
||||||
"sub": "Möchten Sie diesen über einen Proxy öffnen?",
|
"sub": "Möchten Sie diesen über einen Proxy öffnen?",
|
||||||
"button1": "Normal öffnen",
|
"button1": "Normal öffnen",
|
||||||
"button2": "Proxy verwenden"
|
"button2": "Proxy verwenden"
|
||||||
},
|
},
|
||||||
"setting": {
|
"setting": {
|
||||||
"tabs": {
|
"tabs": {
|
||||||
"proxy": "Proxy",
|
"proxy": "Proxy",
|
||||||
"tab": "tab",
|
"tab": "tab",
|
||||||
"custom": "Anpassung",
|
"custom": "Anpassung",
|
||||||
"misc": "Misc"
|
"misc": "Misc"
|
||||||
},
|
},
|
||||||
"proxy": {
|
"proxy": {
|
||||||
"title": "Proxy",
|
"title": "Proxy",
|
||||||
"subtitle": "Wählen Sie den Proxy, der Ihren Anforderungen entspricht",
|
"subtitle": "Wählen Sie den Proxy, der Ihren Anforderungen entspricht",
|
||||||
"automatic": "Automatisch",
|
"automatic": "Automatisch",
|
||||||
"buggyWarning": "(PROBLEME)"
|
"buggyWarning": "(PROBLEME)"
|
||||||
},
|
},
|
||||||
"languages": {
|
"languages": {
|
||||||
"title": "Sprache",
|
"title": "Sprache",
|
||||||
"subtitle": "Wählen Sie Ihre bevorzugte Sprache",
|
"subtitle": "Wählen Sie Ihre bevorzugte Sprache",
|
||||||
"japanese": "Japanisch",
|
"japanese": "Japanisch",
|
||||||
"english": "Englisch",
|
"english": "Englisch",
|
||||||
"spanish": "Spanisch",
|
"spanish": "Spanisch",
|
||||||
"german": "Deutsch",
|
"german": "Deutsch",
|
||||||
"greek": "Griechisch",
|
"greek": "Griechisch",
|
||||||
"dutch": "Niederländisch"
|
"dutch": "Niederländisch"
|
||||||
},
|
},
|
||||||
"proxymodes": {
|
"proxymodes": {
|
||||||
"title": "Öffnen in",
|
"title": "Öffnen in",
|
||||||
"subtitle": "Wählen Sie, wie Ihre Seiten geöffnet werden sollen",
|
"subtitle": "Wählen Sie, wie Ihre Seiten geöffnet werden sollen",
|
||||||
"embed": "Einbetten",
|
"embed": "Einbetten",
|
||||||
"direct": "Direkt",
|
"direct": "Direkt",
|
||||||
"aboutblank": "About:Blank"
|
"aboutblank": "About:Blank"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,52 +1,52 @@
|
||||||
{
|
{
|
||||||
"header": {
|
"header": {
|
||||||
"title": "nebula.",
|
"title": "nebula.",
|
||||||
"games": "Games",
|
"games": "Games",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"discord": "Want more links?"
|
"discord": "Want more links?"
|
||||||
},
|
},
|
||||||
"404": {
|
"404": {
|
||||||
"text": "This Nebula Service has been disabled.",
|
"text": "This Nebula Service has been disabled.",
|
||||||
"return": "Back to home"
|
"return": "Back to home"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"placeholder": "Search the web freely"
|
"placeholder": "Search the web freely"
|
||||||
},
|
},
|
||||||
"discord": {
|
"discord": {
|
||||||
"title": "Nebula's Discord Server",
|
"title": "Nebula's Discord Server",
|
||||||
"sub": "Would you like to open this via proxy?",
|
"sub": "Would you like to open this via proxy?",
|
||||||
"button1": "Open Normally",
|
"button1": "Open Normally",
|
||||||
"button2": "Use Proxy"
|
"button2": "Use Proxy"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"tabs": {
|
"tabs": {
|
||||||
"proxy": "Proxy",
|
"proxy": "Proxy",
|
||||||
"tab": "Tab",
|
"tab": "Tab",
|
||||||
"custom": "Customization",
|
"custom": "Customization",
|
||||||
"misc": "Misc"
|
"misc": "Misc"
|
||||||
},
|
},
|
||||||
"proxy": {
|
"proxy": {
|
||||||
"title": "Proxy",
|
"title": "Proxy",
|
||||||
"subtitle": "Choose the proxy that fits your needs",
|
"subtitle": "Choose the proxy that fits your needs",
|
||||||
"automatic": "Automatic",
|
"automatic": "Automatic",
|
||||||
"buggyWarning": "(BUGGY)"
|
"buggyWarning": "(BUGGY)"
|
||||||
},
|
},
|
||||||
"languages": {
|
"languages": {
|
||||||
"title": "Language",
|
"title": "Language",
|
||||||
"subtitle": "Choose your preferred language",
|
"subtitle": "Choose your preferred language",
|
||||||
"japanese": "Japanese",
|
"japanese": "Japanese",
|
||||||
"english": "English",
|
"english": "English",
|
||||||
"spanish": "Spanish",
|
"spanish": "Spanish",
|
||||||
"german": "German",
|
"german": "German",
|
||||||
"greek": "Greek",
|
"greek": "Greek",
|
||||||
"dutch": "Dutch"
|
"dutch": "Dutch"
|
||||||
},
|
},
|
||||||
"proxymodes": {
|
"proxymodes": {
|
||||||
"title": "Open in",
|
"title": "Open in",
|
||||||
"subtitle": "Choose how to open your sites",
|
"subtitle": "Choose how to open your sites",
|
||||||
"embed": "Embed",
|
"embed": "Embed",
|
||||||
"direct": "Direct",
|
"direct": "Direct",
|
||||||
"aboutblank": "About:Blank"
|
"aboutblank": "About:Blank"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,52 +1,52 @@
|
||||||
{
|
{
|
||||||
"header": {
|
"header": {
|
||||||
"title": "nebula.",
|
"title": "nebula.",
|
||||||
"games": "Juegos",
|
"games": "Juegos",
|
||||||
"settings": "Ajustes",
|
"settings": "Ajustes",
|
||||||
"discord": "¿Quieres más enlaces?"
|
"discord": "¿Quieres más enlaces?"
|
||||||
},
|
},
|
||||||
"404": {
|
"404": {
|
||||||
"text": "Este servicio Nebula ha sido deshabilitado.",
|
"text": "Este servicio Nebula ha sido deshabilitado.",
|
||||||
"return": "De vuelta a casa"
|
"return": "De vuelta a casa"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"placeholder": "Busca en la web libremente"
|
"placeholder": "Busca en la web libremente"
|
||||||
},
|
},
|
||||||
"discord": {
|
"discord": {
|
||||||
"title": "Nebula's Discord Servidor",
|
"title": "Nebula's Discord Servidor",
|
||||||
"sub": "¿Le gustaría abrir esto a través de proxy?",
|
"sub": "¿Le gustaría abrir esto a través de proxy?",
|
||||||
"button1": "Abierto normalmente",
|
"button1": "Abierto normalmente",
|
||||||
"button2": "Usa proxy"
|
"button2": "Usa proxy"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"tabs": {
|
"tabs": {
|
||||||
"proxy": "Proxy",
|
"proxy": "Proxy",
|
||||||
"tab": "Pestaña",
|
"tab": "Pestaña",
|
||||||
"custom": "Personalización",
|
"custom": "Personalización",
|
||||||
"misc": "Misc"
|
"misc": "Misc"
|
||||||
},
|
},
|
||||||
"proxy": {
|
"proxy": {
|
||||||
"title": "Proxy",
|
"title": "Proxy",
|
||||||
"subtitle": "Elige el proxy que se ajuste a tus necesidades",
|
"subtitle": "Elige el proxy que se ajuste a tus necesidades",
|
||||||
"automatic": "Automática",
|
"automatic": "Automática",
|
||||||
"buggyWarning": "(CALESA)"
|
"buggyWarning": "(CALESA)"
|
||||||
},
|
},
|
||||||
"languages": {
|
"languages": {
|
||||||
"title": "Idioma",
|
"title": "Idioma",
|
||||||
"subtitle": "Elige tu idioma preferido",
|
"subtitle": "Elige tu idioma preferido",
|
||||||
"japanese": "Japonés",
|
"japanese": "Japonés",
|
||||||
"english": "Inglés",
|
"english": "Inglés",
|
||||||
"spanish": "Español",
|
"spanish": "Español",
|
||||||
"german": "",
|
"german": "",
|
||||||
"greek": "",
|
"greek": "",
|
||||||
"dutch": ""
|
"dutch": ""
|
||||||
},
|
},
|
||||||
"proxymodes": {
|
"proxymodes": {
|
||||||
"title": "Abrir en",
|
"title": "Abrir en",
|
||||||
"subtitle": "Elige cómo abrir tus sitios",
|
"subtitle": "Elige cómo abrir tus sitios",
|
||||||
"embed": "Incrustar",
|
"embed": "Incrustar",
|
||||||
"direct": "Directo",
|
"direct": "Directo",
|
||||||
"aboutblank": "About:Blank"
|
"aboutblank": "About:Blank"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,52 +1,52 @@
|
||||||
{
|
{
|
||||||
"header": {
|
"header": {
|
||||||
"title": "νεφέλωμα",
|
"title": "νεφέλωμα",
|
||||||
"games": "Παιχνίδια",
|
"games": "Παιχνίδια",
|
||||||
"settings": "Ρυθμίσεις",
|
"settings": "Ρυθμίσεις",
|
||||||
"discord": "θέλετε περισσότερους συνδέσμους?"
|
"discord": "θέλετε περισσότερους συνδέσμους?"
|
||||||
},
|
},
|
||||||
"404": {
|
"404": {
|
||||||
"text": "Αυτή η υπηρεσία Νεφέλωμα έχει απενεργοποιηθεί.",
|
"text": "Αυτή η υπηρεσία Νεφέλωμα έχει απενεργοποιηθεί.",
|
||||||
"return": "Επιστροφή στο σπίτι."
|
"return": "Επιστροφή στο σπίτι."
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"placeholder": "Ελεύθερη αναζήτηση στο διαδίκτυο."
|
"placeholder": "Ελεύθερη αναζήτηση στο διαδίκτυο."
|
||||||
},
|
},
|
||||||
"discord": {
|
"discord": {
|
||||||
"title": "Discord Διακομιστής του Νεφελώματος",
|
"title": "Discord Διακομιστής του Νεφελώματος",
|
||||||
"sub": "Άνοιγμα μέσω διακομιστή μεσολάβησης;",
|
"sub": "Άνοιγμα μέσω διακομιστή μεσολάβησης;",
|
||||||
"button1": "Ανοιχτό κανονικά",
|
"button1": "Ανοιχτό κανονικά",
|
||||||
"button2": "Χρήση μεσολάβησης"
|
"button2": "Χρήση μεσολάβησης"
|
||||||
},
|
},
|
||||||
"setting": {
|
"setting": {
|
||||||
"tabs": {
|
"tabs": {
|
||||||
"proxy": "Mεσολάβησης",
|
"proxy": "Mεσολάβησης",
|
||||||
"tab": "Καρτέλα",
|
"tab": "Καρτέλα",
|
||||||
"custom": "Προσαρμογή",
|
"custom": "Προσαρμογή",
|
||||||
"misc": "Διάφορα"
|
"misc": "Διάφορα"
|
||||||
},
|
},
|
||||||
"proxy": {
|
"proxy": {
|
||||||
"title": "Μεσολάβηση",
|
"title": "Μεσολάβηση",
|
||||||
"subtitle": "Επιλέξτε τη μεσολάβηση που ταιριάζει στις ανάγκες σας",
|
"subtitle": "Επιλέξτε τη μεσολάβηση που ταιριάζει στις ανάγκες σας",
|
||||||
"automatic": "Αυτόματο",
|
"automatic": "Αυτόματο",
|
||||||
"buggyWarning": "(ΠΡΟΒΛΗΜΑ)"
|
"buggyWarning": "(ΠΡΟΒΛΗΜΑ)"
|
||||||
},
|
},
|
||||||
"languages": {
|
"languages": {
|
||||||
"title": "Γλώσσες",
|
"title": "Γλώσσες",
|
||||||
"subtitle": "Επιλέξτε τη γλώσσα που προτιμάτε",
|
"subtitle": "Επιλέξτε τη γλώσσα που προτιμάτε",
|
||||||
"japanese": "Ιαπωνικά",
|
"japanese": "Ιαπωνικά",
|
||||||
"english": "Αγγλικά",
|
"english": "Αγγλικά",
|
||||||
"spanish": "Ησπανικά",
|
"spanish": "Ησπανικά",
|
||||||
"german": "Γερμανικό",
|
"german": "Γερμανικό",
|
||||||
"greek": "Ελληνικά",
|
"greek": "Ελληνικά",
|
||||||
"dutch": "Ολλανδικά"
|
"dutch": "Ολλανδικά"
|
||||||
},
|
},
|
||||||
"proxymodes": {
|
"proxymodes": {
|
||||||
"title": "Άνοιγμα σε",
|
"title": "Άνοιγμα σε",
|
||||||
"subtitle": "Επιλέξτε τον τρόπο ανοίγματος των ιστοσελίδων σας",
|
"subtitle": "Επιλέξτε τον τρόπο ανοίγματος των ιστοσελίδων σας",
|
||||||
"embed": "εΕνσωμάτωση",
|
"embed": "εΕνσωμάτωση",
|
||||||
"direct": "¨Αμεσα",
|
"direct": "¨Αμεσα",
|
||||||
"aboutblank": "About:Blank"
|
"aboutblank": "About:Blank"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,52 +1,52 @@
|
||||||
{
|
{
|
||||||
"header": {
|
"header": {
|
||||||
"title": "ネブラ。",
|
"title": "ネブラ。",
|
||||||
"games": "ゲーム",
|
"games": "ゲーム",
|
||||||
"settings": "セッティング",
|
"settings": "セッティング",
|
||||||
"discord": "もっとリンクが欲しいですか?"
|
"discord": "もっとリンクが欲しいですか?"
|
||||||
},
|
},
|
||||||
"404": {
|
"404": {
|
||||||
"text": "このネブラサービスは無効になっています",
|
"text": "このネブラサービスは無効になっています",
|
||||||
"return": "帰宅"
|
"return": "帰宅"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"placeholder": "由にウェブを検索"
|
"placeholder": "由にウェブを検索"
|
||||||
},
|
},
|
||||||
"discord": {
|
"discord": {
|
||||||
"title": "ネブラDiscordサーバー",
|
"title": "ネブラDiscordサーバー",
|
||||||
"sub": "プロキシ経由で開きますか?",
|
"sub": "プロキシ経由で開きますか?",
|
||||||
"button1": "通常通り開く",
|
"button1": "通常通り開く",
|
||||||
"button2": "プロキシを使用する"
|
"button2": "プロキシを使用する"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"tabs": {
|
"tabs": {
|
||||||
"proxy": "プロキシ",
|
"proxy": "プロキシ",
|
||||||
"tab": "タブ",
|
"tab": "タブ",
|
||||||
"custom": "カスタマイズ",
|
"custom": "カスタマイズ",
|
||||||
"misc": "その他"
|
"misc": "その他"
|
||||||
},
|
},
|
||||||
"proxy": {
|
"proxy": {
|
||||||
"title": "プロキシ",
|
"title": "プロキシ",
|
||||||
"subtitle": "ニーズに合ったプロキシを選んでください",
|
"subtitle": "ニーズに合ったプロキシを選んでください",
|
||||||
"automatic": "自動",
|
"automatic": "自動",
|
||||||
"buggyWarning": "(バグ)"
|
"buggyWarning": "(バグ)"
|
||||||
},
|
},
|
||||||
"languages": {
|
"languages": {
|
||||||
"title": "言語",
|
"title": "言語",
|
||||||
"subtitle": "好きな言語を選んでください",
|
"subtitle": "好きな言語を選んでください",
|
||||||
"japanese": "日本語",
|
"japanese": "日本語",
|
||||||
"english": "英語",
|
"english": "英語",
|
||||||
"spanish": "スペイン語",
|
"spanish": "スペイン語",
|
||||||
"german": "",
|
"german": "",
|
||||||
"greek": "",
|
"greek": "",
|
||||||
"dutch": ""
|
"dutch": ""
|
||||||
},
|
},
|
||||||
"proxymodes": {
|
"proxymodes": {
|
||||||
"title": "開く",
|
"title": "開く",
|
||||||
"subtitle": "サイトを開く方法を選択する",
|
"subtitle": "サイトを開く方法を選択する",
|
||||||
"embed": "埋め込む",
|
"embed": "埋め込む",
|
||||||
"direct": "直接",
|
"direct": "直接",
|
||||||
"aboutblank": "About:Blank"
|
"aboutblank": "About:Blank"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,55 +1,55 @@
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { HeaderRoute } from "../components/HeaderRoute";
|
import { HeaderRoute } from "../components/HeaderRoute";
|
||||||
import { Helmet } from "react-helmet";
|
import { Helmet } from "react-helmet";
|
||||||
|
|
||||||
export function Home() {
|
export function Home() {
|
||||||
const [isFocused, setIsFocused] = useState(false);
|
const [isFocused, setIsFocused] = useState(false);
|
||||||
const [inputValue, setInputValue] = useState("");
|
const [inputValue, setInputValue] = useState("");
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const handleSubmit = (event) => {
|
const handleSubmit = (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
window.location.href = "/go/" + encodeURIComponent(inputValue);
|
window.location.href = "/go/" + encodeURIComponent(inputValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HeaderRoute>
|
<HeaderRoute>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>Nebula</title>
|
<title>Nebula</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<div class="flex h-full flex-col items-center justify-center">
|
<div class="flex h-full flex-col items-center justify-center">
|
||||||
<div class="font-inter absolute bottom-0 left-0 p-1 text-sm italic text-input-text">
|
<div class="font-inter absolute bottom-0 left-0 p-1 text-sm italic text-input-text">
|
||||||
Nebula © Nebula Services {new Date().getUTCFullYear()}
|
Nebula © Nebula Services {new Date().getUTCFullYear()}
|
||||||
</div>
|
</div>
|
||||||
<a href="https://github.com/NebulaServices/Nebula">
|
<a href="https://github.com/NebulaServices/Nebula">
|
||||||
<div class="font-inter absolute bottom-0 right-0 p-1 text-sm text-input-text">
|
<div class="font-inter absolute bottom-0 right-0 p-1 text-sm text-input-text">
|
||||||
GitHub
|
GitHub
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<form
|
<form
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
class="flex h-full w-full items-center justify-center"
|
class="flex h-full w-full items-center justify-center"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
onFocus={() => {
|
onFocus={() => {
|
||||||
setIsFocused(true);
|
setIsFocused(true);
|
||||||
}}
|
}}
|
||||||
onBlur={() => {
|
onBlur={() => {
|
||||||
setIsFocused(false);
|
setIsFocused(false);
|
||||||
}}
|
}}
|
||||||
type="text"
|
type="text"
|
||||||
value={inputValue}
|
value={inputValue}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setInputValue((e.target as HTMLInputElement).value)
|
setInputValue((e.target as HTMLInputElement).value)
|
||||||
}
|
}
|
||||||
className={`font-roboto h-14 rounded-2xl border border-input-border-color bg-input p-2 text-center text-xl placeholder:text-input-text focus:outline-none ${
|
className={`font-roboto h-14 rounded-2xl border border-input-border-color bg-input p-2 text-center text-xl placeholder:text-input-text focus:outline-none ${
|
||||||
isFocused ? "w-full md:w-3/12" : "w-full md:w-80"
|
isFocused ? "w-full md:w-3/12" : "w-full md:w-80"
|
||||||
} transition-all duration-300`}
|
} transition-all duration-300`}
|
||||||
placeholder={isFocused ? "" : t("home.placeholder")}
|
placeholder={isFocused ? "" : t("home.placeholder")}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</HeaderRoute>
|
</HeaderRoute>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,84 +1,85 @@
|
||||||
import { RammerheadEncode } from "../util/RammerheadEncode";
|
import { RammerheadEncode } from "../util/RammerheadEncode";
|
||||||
import { searchUtil } from "../util/searchUtil";
|
import { searchUtil } from "../util/searchUtil";
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
//import our Iframe component
|
//import our Iframe component
|
||||||
import { Iframe } from "../components/iframe/Iframe";
|
import { Iframe } from "../components/iframe/Iframe";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
__uv$config: any;
|
__uv$config: any;
|
||||||
__dynamic$config: any;
|
__dynamic$config: any;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ProxyFrame(props: { url: string }) {
|
export function ProxyFrame(props: { url: string }) {
|
||||||
// pass the URL encoded with encodeURIcomponent
|
// pass the URL encoded with encodeURIcomponent
|
||||||
const localProxy = localStorage.getItem("proxy") || "automatic";
|
const localProxy = localStorage.getItem("proxy") || "automatic";
|
||||||
const proxyMode = localStorage.getItem("proxyMode") || "direct";
|
const proxyMode = localStorage.getItem("proxyMode") || "direct";
|
||||||
|
|
||||||
const [ProxiedUrl, setProxiedUrl] = useState<string | undefined>(undefined);
|
const [ProxiedUrl, setProxiedUrl] = useState<string | undefined>(undefined);
|
||||||
|
|
||||||
let decodedUrl = decodeURIComponent(props.url);
|
let decodedUrl = decodeURIComponent(props.url);
|
||||||
//attempt to convert to a valid url
|
//attempt to convert to a valid url
|
||||||
decodedUrl = searchUtil(decodedUrl, "https://google.com/search?q=%s");
|
decodedUrl = searchUtil(decodedUrl, "https://google.com/search?q=%s");
|
||||||
|
|
||||||
let proxyRef;
|
let proxyRef;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// For now we can redirect to the results. In the future we will add an if statement looking for the users proxy display choice
|
// For now we can redirect to the results. In the future we will add an if statement looking for the users proxy display choice
|
||||||
if (localProxy === "rammerhead") {
|
if (localProxy === "rammerhead") {
|
||||||
RammerheadEncode(decodedUrl).then((result: string) => {
|
RammerheadEncode(decodedUrl).then((result: string) => {
|
||||||
setProxiedUrl(result);
|
setProxiedUrl(result);
|
||||||
});
|
});
|
||||||
} else if (localProxy === "ultraviolet") {
|
} else if (localProxy === "ultraviolet") {
|
||||||
setProxiedUrl(
|
setProxiedUrl(
|
||||||
window.__uv$config.prefix + window.__uv$config.encodeUrl(decodedUrl)
|
window.__uv$config.prefix + window.__uv$config.encodeUrl(decodedUrl)
|
||||||
);
|
);
|
||||||
} else if (localProxy === "dynamic") {
|
} else if (localProxy === "dynamic") {
|
||||||
setProxiedUrl(
|
setProxiedUrl(
|
||||||
window.__dynamic$config.prefix + encodeURIComponent(decodedUrl)
|
window.__dynamic$config.prefix + encodeURIComponent(decodedUrl)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// use UV for automatic
|
// use UV for automatic
|
||||||
setProxiedUrl(
|
setProxiedUrl(
|
||||||
window.__uv$config.prefix + window.__uv$config.encodeUrl(decodedUrl)
|
window.__uv$config.prefix + window.__uv$config.encodeUrl(decodedUrl)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [localProxy]);
|
}, [localProxy]);
|
||||||
|
|
||||||
if (proxyMode == "direct") {
|
if (proxyMode == "direct") {
|
||||||
window.location.href = ProxiedUrl;
|
window.location.href = ProxiedUrl;
|
||||||
}
|
} else if (proxyMode == "aboutblank") {
|
||||||
else if (proxyMode == "aboutblank") {
|
const newWindow = window.open("about:blank", "_blank");
|
||||||
const newWindow = window.open("about:blank", "_blank");
|
const newDocument = newWindow.document.open();
|
||||||
const newDocument = newWindow.document.open();
|
newDocument.write(`
|
||||||
newDocument.write(`
|
<!DOCTYPE html>
|
||||||
<!DOCTYPE html>
|
<html>
|
||||||
<html>
|
<head>
|
||||||
<head>
|
<style type="text/css">
|
||||||
<style type="text/css">
|
body, html
|
||||||
body, html
|
{
|
||||||
{
|
margin: 0; padding: 0; height: 100%; overflow: hidden;
|
||||||
margin: 0; padding: 0; height: 100%; overflow: hidden;
|
}
|
||||||
}
|
</style>
|
||||||
</style>
|
</head>
|
||||||
</head>
|
<body>
|
||||||
<body>
|
<iframe style="border: none; width: 100%; height: 100vh;" src="${
|
||||||
<iframe style="border: none; width: 100%; height: 100vh;" src="${
|
window.location.origin + ProxiedUrl
|
||||||
window.location.origin + ProxiedUrl
|
}"></iframe>
|
||||||
}"></iframe>
|
</body>
|
||||||
</body>
|
</html>
|
||||||
</html>
|
`);
|
||||||
`);
|
newDocument.close();
|
||||||
newDocument.close()
|
window.location.replace("/");
|
||||||
window.location.replace("/");
|
}
|
||||||
}
|
|
||||||
|
return (
|
||||||
return (
|
<div class="h-screen w-screen bg-primary">
|
||||||
<div class="h-screen w-screen bg-primary">
|
{proxyMode === "direct" && <h1>Loading {localProxy}...</h1>}
|
||||||
{proxyMode === "direct" && <h1>Loading {localProxy}...</h1>}
|
{proxyMode === "aboutblank" && <h1>Loading {localProxy}...</h1>}
|
||||||
{proxyMode === "aboutblank" && <h1>Loading {localProxy}...</h1>}
|
{proxyMode === "embed" && (
|
||||||
{proxyMode === "embed" && <Iframe url={ProxiedUrl} normalUrl={decodedUrl} />}
|
<Iframe url={ProxiedUrl} normalUrl={decodedUrl} />
|
||||||
</div>
|
)}
|
||||||
);
|
</div>
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { tabContentVariant, settingsPageVariant } from "./Variants";
|
import { tabContentVariant, settingsPageVariant } from "./Variants";
|
||||||
|
|
||||||
const Customization = ({ id, active }) => (
|
const Customization = ({ id, active }) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
role="tabpanel"
|
role="tabpanel"
|
||||||
id={id}
|
id={id}
|
||||||
className="tab-content"
|
className="tab-content"
|
||||||
variants={tabContentVariant}
|
variants={tabContentVariant}
|
||||||
animate={active ? "active" : "inactive"}
|
animate={active ? "active" : "inactive"}
|
||||||
initial="inactive"
|
initial="inactive"
|
||||||
>
|
>
|
||||||
<motion.div variants={settingsPageVariant} className="content-card">
|
<motion.div variants={settingsPageVariant} className="content-card">
|
||||||
<h1>Customization</h1>
|
<h1>Customization</h1>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default Customization;
|
export default Customization;
|
||||||
|
|
|
@ -1,73 +1,73 @@
|
||||||
import { useState, useEffect } from "preact/hooks";
|
import { useState, useEffect } from "preact/hooks";
|
||||||
import { FaAngleDown } from "react-icons/fa";
|
import { FaAngleDown } from "react-icons/fa";
|
||||||
|
|
||||||
interface Option {
|
interface Option {
|
||||||
id: string;
|
id: string;
|
||||||
label: string; // Translations CAN be passed
|
label: string; // Translations CAN be passed
|
||||||
}
|
}
|
||||||
|
|
||||||
const Dropdown = ({
|
const Dropdown = ({
|
||||||
storageKey,
|
storageKey,
|
||||||
options,
|
options,
|
||||||
refresh
|
refresh
|
||||||
}: {
|
}: {
|
||||||
storageKey: string;
|
storageKey: string;
|
||||||
options: Option[];
|
options: Option[];
|
||||||
refresh: boolean;
|
refresh: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
const [choice, setChoice] = useState(() => {
|
const [choice, setChoice] = useState(() => {
|
||||||
return localStorage.getItem(storageKey) || options[0]?.id || "";
|
return localStorage.getItem(storageKey) || options[0]?.id || "";
|
||||||
});
|
});
|
||||||
|
|
||||||
// update on localstorage change
|
// update on localstorage change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setChoice(localStorage.getItem(storageKey) || options[0]?.id || "");
|
setChoice(localStorage.getItem(storageKey) || options[0]?.id || "");
|
||||||
}, [storageKey, options]);
|
}, [storageKey, options]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative text-center">
|
<div className="relative text-center">
|
||||||
<div
|
<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 ${
|
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"
|
isOpen ? "rounded-t-2xl" : "rounded-2xl"
|
||||||
}`}
|
}`}
|
||||||
onClick={() => setIsOpen(!isOpen)}
|
onClick={() => setIsOpen(!isOpen)}
|
||||||
>
|
>
|
||||||
<div className="flex h-full w-full select-none flex-row items-center">
|
<div className="flex h-full w-full select-none flex-row items-center">
|
||||||
<div class="h-full w-1/4"></div>
|
<div class="h-full w-1/4"></div>
|
||||||
<div class="flex w-2/4 flex-col items-center">
|
<div class="flex w-2/4 flex-col items-center">
|
||||||
{options.find((o) => o.id === choice)?.label}
|
{options.find((o) => o.id === choice)?.label}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex w-1/4 flex-col items-center">
|
<div class="flex w-1/4 flex-col items-center">
|
||||||
<FaAngleDown />
|
<FaAngleDown />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<div className="absolute top-full w-full">
|
<div className="absolute top-full w-full">
|
||||||
{options.map((option, index) => (
|
{options.map((option, index) => (
|
||||||
<div
|
<div
|
||||||
key={option.id}
|
key={option.id}
|
||||||
className={`border border-input-border-color bg-input p-2 hover:bg-dropdown-option-hover-color ${
|
className={`border border-input-border-color bg-input p-2 hover:bg-dropdown-option-hover-color ${
|
||||||
index === options.length - 1 ? "rounded-b-2xl" : ""
|
index === options.length - 1 ? "rounded-b-2xl" : ""
|
||||||
}`}
|
}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
setChoice(option.id);
|
setChoice(option.id);
|
||||||
localStorage.setItem(storageKey, option.id);
|
localStorage.setItem(storageKey, option.id);
|
||||||
if (refresh === true) {
|
if (refresh === true) {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{option.label}
|
{option.label}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Dropdown;
|
export default Dropdown;
|
||||||
|
|
|
@ -1,41 +1,41 @@
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { tabContentVariant, settingsPageVariant } from "./Variants";
|
import { tabContentVariant, settingsPageVariant } from "./Variants";
|
||||||
import Dropdown from "./Dropdown";
|
import Dropdown from "./Dropdown";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const Misc = ({ id, active }) => {
|
const Misc = ({ id, active }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const languages = [
|
const languages = [
|
||||||
{ id: "en-US", label: t("settings.languages.english") },
|
{ id: "en-US", label: t("settings.languages.english") },
|
||||||
{ id: "es", label: t("settings.languages.spanish") },
|
{ id: "es", label: t("settings.languages.spanish") },
|
||||||
{ id: "ja", label: t("settings.languages.japanese") }
|
{ id: "ja", label: t("settings.languages.japanese") }
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
role="tabpanel"
|
role="tabpanel"
|
||||||
id={id}
|
id={id}
|
||||||
className="tab-content"
|
className="tab-content"
|
||||||
variants={tabContentVariant}
|
variants={tabContentVariant}
|
||||||
animate={active ? "active" : "inactive"}
|
animate={active ? "active" : "inactive"}
|
||||||
initial="inactive"
|
initial="inactive"
|
||||||
>
|
>
|
||||||
<motion.div
|
<motion.div
|
||||||
variants={settingsPageVariant}
|
variants={settingsPageVariant}
|
||||||
className="content-card flex flex-row flex-wrap justify-around"
|
className="content-card flex flex-row flex-wrap justify-around"
|
||||||
>
|
>
|
||||||
<div class="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 class="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 class="text-3xl">{t("settings.languages.title")}</div>
|
<div class="text-3xl">{t("settings.languages.title")}</div>
|
||||||
<div class="text-md">{t("settings.languages.subtitle")}</div>
|
<div class="text-md">{t("settings.languages.subtitle")}</div>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
storageKey="i18nextLng"
|
storageKey="i18nextLng"
|
||||||
options={languages}
|
options={languages}
|
||||||
refresh={true}
|
refresh={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default Misc;
|
export default Misc;
|
||||||
|
|
|
@ -1,49 +1,53 @@
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { tabContentVariant, settingsPageVariant } from "./Variants";
|
import { tabContentVariant, settingsPageVariant } from "./Variants";
|
||||||
import Dropdown from "./Dropdown";
|
import Dropdown from "./Dropdown";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const Proxy = ({ id, active }) => {
|
const Proxy = ({ id, active }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const engines = [
|
const engines = [
|
||||||
{ id: "automatic", label: t("settings.proxy.automatic") },
|
{ id: "automatic", label: t("settings.proxy.automatic") },
|
||||||
{ id: "ultraviolet", label: "Ultraviolet" },
|
{ id: "ultraviolet", label: "Ultraviolet" },
|
||||||
{ id: "rammerhead", label: "Rammerhead" },
|
{ id: "rammerhead", label: "Rammerhead" },
|
||||||
{ id: "dynamic", label: "Dynamic " + t("settings.proxy.buggyWarning") }
|
{ id: "dynamic", label: "Dynamic " + t("settings.proxy.buggyWarning") }
|
||||||
];
|
];
|
||||||
|
|
||||||
const proxyModes = [
|
const proxyModes = [
|
||||||
{ id: "direct", label: t("settings.proxymodes.direct") },
|
{ id: "direct", label: t("settings.proxymodes.direct") },
|
||||||
{ id: "embed", label: t("settings.proxymodes.embed") },
|
{ id: "embed", label: t("settings.proxymodes.embed") },
|
||||||
{ id: "aboutblank", label: t("settings.proxymodes.aboutblank") }
|
{ id: "aboutblank", label: t("settings.proxymodes.aboutblank") }
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
role="tabpanel"
|
role="tabpanel"
|
||||||
id={id}
|
id={id}
|
||||||
className="tab-content"
|
className="tab-content"
|
||||||
variants={tabContentVariant}
|
variants={tabContentVariant}
|
||||||
animate={active ? "active" : "inactive"}
|
animate={active ? "active" : "inactive"}
|
||||||
initial="inactive"
|
initial="inactive"
|
||||||
>
|
>
|
||||||
<motion.div
|
<motion.div
|
||||||
variants={settingsPageVariant}
|
variants={settingsPageVariant}
|
||||||
className="content-card flex flex-row flex-wrap justify-left w-full gap-4"
|
className="content-card justify-left flex w-full flex-row flex-wrap gap-4"
|
||||||
>
|
>
|
||||||
<div class="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 class="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 class="p-2 text-3xl">{t("settings.proxy.title")}</div>
|
<div class="p-2 text-3xl">{t("settings.proxy.title")}</div>
|
||||||
<div class="text-md p-4">{t("settings.proxy.subtitle")}</div>
|
<div class="text-md p-4">{t("settings.proxy.subtitle")}</div>
|
||||||
<Dropdown storageKey="proxy" options={engines} refresh={false} />
|
<Dropdown storageKey="proxy" options={engines} refresh={false} />
|
||||||
</div>
|
</div>
|
||||||
<div class="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 class="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 class="p-2 text-3xl">{t("settings.proxymodes.title")}</div>
|
<div class="p-2 text-3xl">{t("settings.proxymodes.title")}</div>
|
||||||
<div class="text-md p-4">{t("settings.proxymodes.subtitle")}</div>
|
<div class="text-md p-4">{t("settings.proxymodes.subtitle")}</div>
|
||||||
<Dropdown storageKey="proxyMode" options={proxyModes} refresh={false} />
|
<Dropdown
|
||||||
</div>
|
storageKey="proxyMode"
|
||||||
</motion.div>
|
options={proxyModes}
|
||||||
</motion.div>
|
refresh={false}
|
||||||
);
|
/>
|
||||||
};
|
</div>
|
||||||
export default Proxy;
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default Proxy;
|
||||||
|
|
|
@ -1,106 +1,106 @@
|
||||||
import { useState, useEffect } from "preact/hooks";
|
import { useState, useEffect } from "preact/hooks";
|
||||||
import cn from "classnames";
|
import cn from "classnames";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import "./styles.css";
|
import "./styles.css";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const tabVariant = {
|
const tabVariant = {
|
||||||
active: {
|
active: {
|
||||||
width: "55%",
|
width: "55%",
|
||||||
transition: {
|
transition: {
|
||||||
type: "tween",
|
type: "tween",
|
||||||
duration: 0.4
|
duration: 0.4
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inactive: {
|
inactive: {
|
||||||
width: "15%",
|
width: "15%",
|
||||||
transition: {
|
transition: {
|
||||||
type: "tween",
|
type: "tween",
|
||||||
duration: 0.4
|
duration: 0.4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const tabTextVariant = {
|
const tabTextVariant = {
|
||||||
active: {
|
active: {
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
x: 0,
|
x: 0,
|
||||||
display: "block",
|
display: "block",
|
||||||
transition: {
|
transition: {
|
||||||
type: "tween",
|
type: "tween",
|
||||||
duration: 0.3,
|
duration: 0.3,
|
||||||
delay: 0.3
|
delay: 0.3
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inactive: {
|
inactive: {
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
x: -30,
|
x: -30,
|
||||||
transition: {
|
transition: {
|
||||||
type: "tween",
|
type: "tween",
|
||||||
duration: 0.3,
|
duration: 0.3,
|
||||||
delay: 0.1
|
delay: 0.1
|
||||||
},
|
},
|
||||||
transitionEnd: { display: "none" }
|
transitionEnd: { display: "none" }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const TabComponent = ({ tabs, defaultIndex = 0 }) => {
|
const TabComponent = ({ tabs, defaultIndex = 0 }) => {
|
||||||
const [activeTabIndex, setActiveTabIndex] = useState(defaultIndex);
|
const [activeTabIndex, setActiveTabIndex] = useState(defaultIndex);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.documentElement.style.setProperty(
|
document.documentElement.style.setProperty(
|
||||||
"--active-color",
|
"--active-color",
|
||||||
tabs[activeTabIndex].color
|
tabs[activeTabIndex].color
|
||||||
);
|
);
|
||||||
}, [activeTabIndex, tabs]);
|
}, [activeTabIndex, tabs]);
|
||||||
|
|
||||||
// Default to a tab based on the URL hash value
|
// Default to a tab based on the URL hash value
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const tabFromHash = tabs.findIndex(
|
const tabFromHash = tabs.findIndex(
|
||||||
(tab) => `#${tab.id}` === window.location.hash
|
(tab) => `#${tab.id}` === window.location.hash
|
||||||
);
|
);
|
||||||
setActiveTabIndex(tabFromHash !== -1 ? tabFromHash : defaultIndex);
|
setActiveTabIndex(tabFromHash !== -1 ? tabFromHash : defaultIndex);
|
||||||
}, [tabs, defaultIndex]);
|
}, [tabs, defaultIndex]);
|
||||||
|
|
||||||
const onTabClick = (index) => {
|
const onTabClick = (index) => {
|
||||||
setActiveTabIndex(index);
|
setActiveTabIndex(index);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="flex flex-col items-center w-full h-full">
|
<div class="flex h-full w-full flex-col items-center">
|
||||||
<div className="container h-full w-full">
|
<div className="container h-full w-full">
|
||||||
<div className="tabs-component">
|
<div className="tabs-component">
|
||||||
<ul className="tab-links" role="tablist">
|
<ul className="tab-links" role="tablist">
|
||||||
{tabs.map((tab, index) => (
|
{tabs.map((tab, index) => (
|
||||||
<motion.li
|
<motion.li
|
||||||
key={tab.id}
|
key={tab.id}
|
||||||
className={cn("tab", { active: activeTabIndex === index })}
|
className={cn("tab", { active: activeTabIndex === index })}
|
||||||
role="presentation"
|
role="presentation"
|
||||||
variants={tabVariant}
|
variants={tabVariant}
|
||||||
animate={activeTabIndex === index ? "active" : "inactive"}
|
animate={activeTabIndex === index ? "active" : "inactive"}
|
||||||
>
|
>
|
||||||
<a href={`#${tab.id}`} onClick={() => onTabClick(index)}>
|
<a href={`#${tab.id}`} onClick={() => onTabClick(index)}>
|
||||||
{tab.icon}
|
{tab.icon}
|
||||||
<motion.span variants={tabTextVariant}>
|
<motion.span variants={tabTextVariant}>
|
||||||
{t(tab.title)}
|
{t(tab.title)}
|
||||||
</motion.span>
|
</motion.span>
|
||||||
</a>
|
</a>
|
||||||
</motion.li>
|
</motion.li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
{tabs.map((tab, index) => (
|
{tabs.map((tab, index) => (
|
||||||
<tab.content
|
<tab.content
|
||||||
key={tab.id}
|
key={tab.id}
|
||||||
id={`${tab.id}-content`}
|
id={`${tab.id}-content`}
|
||||||
active={activeTabIndex === index}
|
active={activeTabIndex === index}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TabComponent;
|
export default TabComponent;
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { tabContentVariant, settingsPageVariant } from "./Variants";
|
import { tabContentVariant, settingsPageVariant } from "./Variants";
|
||||||
|
|
||||||
const TabSettings = ({ id, active }) => (
|
const TabSettings = ({ id, active }) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
role="tabpanel"
|
role="tabpanel"
|
||||||
id={id}
|
id={id}
|
||||||
className="tab-content"
|
className="tab-content"
|
||||||
variants={tabContentVariant}
|
variants={tabContentVariant}
|
||||||
animate={active ? "active" : "inactive"}
|
animate={active ? "active" : "inactive"}
|
||||||
initial="inactive"
|
initial="inactive"
|
||||||
>
|
>
|
||||||
<motion.div variants={settingsPageVariant} className="content-card">
|
<motion.div variants={settingsPageVariant} className="content-card">
|
||||||
<h1>Tab settings</h1>
|
<h1>Tab settings</h1>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default TabSettings;
|
export default TabSettings;
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
const tabContentVariant = {
|
const tabContentVariant = {
|
||||||
active: {
|
active: {
|
||||||
display: "block",
|
display: "block",
|
||||||
transition: {
|
transition: {
|
||||||
staggerChildren: 0.2
|
staggerChildren: 0.2
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inactive: {
|
inactive: {
|
||||||
display: "none"
|
display: "none"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const settingsPageVariant = {
|
const settingsPageVariant = {
|
||||||
active: {
|
active: {
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
y: 0,
|
y: 0,
|
||||||
transition: {
|
transition: {
|
||||||
duration: 0.5
|
duration: 0.5
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inactive: {
|
inactive: {
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
y: 10,
|
y: 10,
|
||||||
transition: {
|
transition: {
|
||||||
duration: 0.5
|
duration: 0.5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export { settingsPageVariant, tabContentVariant };
|
export { settingsPageVariant, tabContentVariant };
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import TabComponent from "./TabComponent";
|
import TabComponent from "./TabComponent";
|
||||||
import { HeaderRoute } from "../../components/HeaderRoute";
|
import { HeaderRoute } from "../../components/HeaderRoute";
|
||||||
import tabs from "./tabs";
|
import tabs from "./tabs";
|
||||||
import { Helmet } from "react-helmet"
|
import { Helmet } from "react-helmet";
|
||||||
|
|
||||||
export function Settings() {
|
export function Settings() {
|
||||||
return (
|
return (
|
||||||
<HeaderRoute>
|
<HeaderRoute>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>Settings</title>
|
<title>Settings</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<TabComponent tabs={tabs} />
|
<TabComponent tabs={tabs} />
|
||||||
</HeaderRoute>
|
</HeaderRoute>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,128 +1,128 @@
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--white: #fff;
|
--white: #fff;
|
||||||
--black: #333;
|
--black: #333;
|
||||||
--active-color: #f1f1f1;
|
--active-color: #f1f1f1;
|
||||||
--border-radius: 40px;
|
--border-radius: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
background: var(--active-color);
|
background: var(--active-color);
|
||||||
transition: background 1.5s ease;
|
transition: background 1.5s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs-component {
|
.tabs-component {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
padding: 40px;
|
padding: 40px;
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-links {
|
.tab-links {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0 auto 20px;
|
margin: 0 auto 20px;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab {
|
.tab {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab a {
|
.tab a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: var(--black);
|
color: var(--black);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab::before {
|
.tab::before {
|
||||||
content: "";
|
content: "";
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
background: none;
|
background: none;
|
||||||
transition: background 0.5s ease;
|
transition: background 0.5s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab svg {
|
.tab svg {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
min-width: 30px;
|
min-width: 30px;
|
||||||
fill: var(--black);
|
fill: var(--black);
|
||||||
transition: fill 0.5s ease;
|
transition: fill 0.5s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab.active::before {
|
.tab.active::before {
|
||||||
background: var(--active-color);
|
background: var(--active-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab span {
|
.tab span {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
transition: color 0.5s ease;
|
transition: color 0.5s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab.active span {
|
.tab.active span {
|
||||||
color: var(--active-color);
|
color: var(--active-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab.active svg {
|
.tab.active svg {
|
||||||
fill: var(--active-color);
|
fill: var(--active-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab a {
|
.tab a {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cards {
|
.cards {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-card {
|
.content-card {
|
||||||
width: 48%;
|
width: 48%;
|
||||||
margin-bottom: 26px;
|
margin-bottom: 26px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-card .info::after {
|
.content-card .info::after {
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
bottom: -5px;
|
bottom: -5px;
|
||||||
background: var(--active-color);
|
background: var(--active-color);
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-card img {
|
.content-card img {
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-card h3 {
|
.content-card h3 {
|
||||||
margin: 0 0 5px;
|
margin: 0 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-card .info {
|
.content-card .info {
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,42 +1,42 @@
|
||||||
import Proxy from "./Proxy";
|
import Proxy from "./Proxy";
|
||||||
import TabSettings from "./TabSettings";
|
import TabSettings from "./TabSettings";
|
||||||
import Misc from "./Misc";
|
import Misc from "./Misc";
|
||||||
import Customization from "./Customization";
|
import Customization from "./Customization";
|
||||||
|
|
||||||
import { GoBrowser } from "react-icons/go";
|
import { GoBrowser } from "react-icons/go";
|
||||||
import { AiOutlineLaptop } from "react-icons/ai";
|
import { AiOutlineLaptop } from "react-icons/ai";
|
||||||
import { FaPalette } from "react-icons/fa";
|
import { FaPalette } from "react-icons/fa";
|
||||||
import { FaGear } from "react-icons/fa6";
|
import { FaGear } from "react-icons/fa6";
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
title: "settings.tabs.proxy",
|
title: "settings.tabs.proxy",
|
||||||
id: "proxy",
|
id: "proxy",
|
||||||
icon: <AiOutlineLaptop />,
|
icon: <AiOutlineLaptop />,
|
||||||
color: "#5d5dff",
|
color: "#5d5dff",
|
||||||
content: Proxy
|
content: Proxy
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "settings.tabs.tab",
|
title: "settings.tabs.tab",
|
||||||
id: "tab",
|
id: "tab",
|
||||||
icon: <GoBrowser />,
|
icon: <GoBrowser />,
|
||||||
color: "#67bb67",
|
color: "#67bb67",
|
||||||
content: TabSettings
|
content: TabSettings
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "settings.tabs.custom",
|
title: "settings.tabs.custom",
|
||||||
id: "custom",
|
id: "custom",
|
||||||
icon: <FaPalette />,
|
icon: <FaPalette />,
|
||||||
color: "#63a7c7",
|
color: "#63a7c7",
|
||||||
content: Customization
|
content: Customization
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "settings.tabs.misc",
|
title: "settings.tabs.misc",
|
||||||
id: "misc",
|
id: "misc",
|
||||||
icon: <FaGear />,
|
icon: <FaGear />,
|
||||||
color: "#f56868",
|
color: "#f56868",
|
||||||
content: Misc
|
content: Misc
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
export default tabs;
|
export default tabs;
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link } from "preact-router";
|
import { Link } from "preact-router";
|
||||||
import { HeaderRoute } from "../components/HeaderRoute";
|
import { HeaderRoute } from "../components/HeaderRoute";
|
||||||
|
|
||||||
export function NotFound() {
|
export function NotFound() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HeaderRoute>
|
<HeaderRoute>
|
||||||
<section class="h-full">
|
<section class="h-full">
|
||||||
<div class="flex h-full flex-col items-center justify-center">
|
<div class="flex h-full flex-col items-center justify-center">
|
||||||
<img src="/404.png" class="h-72"></img>
|
<img src="/404.png" class="h-72"></img>
|
||||||
<div class="flex flex-col items-center p-6">
|
<div class="flex flex-col items-center p-6">
|
||||||
<p class="font-roboto text-4xl font-bold">{t("404.text")}</p>
|
<p class="font-roboto text-4xl font-bold">{t("404.text")}</p>
|
||||||
<span class="font-roboto text-3xl">404</span>
|
<span class="font-roboto text-3xl">404</span>
|
||||||
</div>
|
</div>
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<button class="font-roboto h-14 w-44 rounded-2xl border border-input-border-color bg-input p-2 text-center text-xl placeholder:text-input-text focus:outline-none">
|
<button class="font-roboto h-14 w-44 rounded-2xl border border-input-border-color bg-input p-2 text-center text-xl placeholder:text-input-text focus:outline-none">
|
||||||
{t("404.return")}
|
{t("404.return")}
|
||||||
</button>
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</HeaderRoute>
|
</HeaderRoute>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,32 @@
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { HeaderRoute } from "../components/HeaderRoute";
|
import { HeaderRoute } from "../components/HeaderRoute";
|
||||||
|
|
||||||
export function DiscordPage() {
|
export function DiscordPage() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HeaderRoute>
|
<HeaderRoute>
|
||||||
<section class="h-full">
|
<section class="h-full">
|
||||||
<div class="flex h-full flex-col items-center justify-center">
|
<div class="flex h-full flex-col items-center justify-center">
|
||||||
<div class="flex flex-col items-center p-6">
|
<div class="flex flex-col items-center p-6">
|
||||||
<p class="font-roboto text-4xl font-bold">{t("discord.title")}</p>
|
<p class="font-roboto text-4xl font-bold">{t("discord.title")}</p>
|
||||||
<span class="font-roboto text-3xl">{t("discord.sub")}</span>
|
<span class="font-roboto text-3xl">{t("discord.sub")}</span>
|
||||||
</div>
|
</div>
|
||||||
<a href="https://discord.gg/unblocker" class="p-6">
|
<a href="https://discord.gg/unblocker" class="p-6">
|
||||||
<button class="font-roboto h-14 w-56 rounded-2xl border border-input-border-color bg-input p-2 text-center text-xl placeholder:text-input-text focus:outline-none">
|
<button class="font-roboto h-14 w-56 rounded-2xl border border-input-border-color bg-input p-2 text-center text-xl placeholder:text-input-text focus:outline-none">
|
||||||
{t("discord.button1")}
|
{t("discord.button1")}
|
||||||
</button>
|
</button>
|
||||||
</a>
|
</a>
|
||||||
<a href="${window.location.href = '/go/' + encodeURIComponent(https://discord.gg/unblocker)}" class="p-6">
|
<a
|
||||||
<button class="font-roboto h-14 w-56 rounded-2xl border border-input-border-color bg-input p-2 text-center text-xl placeholder:text-input-text focus:outline-none">
|
href="${window.location.href = '/go/' + encodeURIComponent(https://discord.gg/unblocker)}"
|
||||||
{t("discord.button2")}
|
class="p-6"
|
||||||
</button>
|
>
|
||||||
</a>
|
<button class="font-roboto h-14 w-56 rounded-2xl border border-input-border-color bg-input p-2 text-center text-xl placeholder:text-input-text focus:outline-none">
|
||||||
</div>
|
{t("discord.button2")}
|
||||||
</section>
|
</button>
|
||||||
</HeaderRoute>
|
</a>
|
||||||
);
|
</div>
|
||||||
}
|
</section>
|
||||||
|
</HeaderRoute>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;700;900&display=swap");
|
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;700;900&display=swap");
|
||||||
|
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Dongle&family=Roboto:wght@100&display=swap");
|
@import url("https://fonts.googleapis.com/css2?family=Dongle&family=Roboto:wght@100&display=swap");
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--background-primary: #191724;
|
--background-primary: #191724;
|
||||||
--background-lighter: #16121f;
|
--background-lighter: #16121f;
|
||||||
--navbar-color: #26233a;
|
--navbar-color: #26233a;
|
||||||
--navbar-height: 60px;
|
--navbar-height: 60px;
|
||||||
--navbar-text-color: #7967dd;
|
--navbar-text-color: #7967dd;
|
||||||
--navbar-link-color: #e0def4;
|
--navbar-link-color: #e0def4;
|
||||||
--navbar-link-hover-color: gray;
|
--navbar-link-hover-color: gray;
|
||||||
--navbar-font: "Roboto";
|
--navbar-font: "Roboto";
|
||||||
--input-text-color: #e0def4;
|
--input-text-color: #e0def4;
|
||||||
--input-placeholder-color: white;
|
--input-placeholder-color: white;
|
||||||
--input-background-color: #1f1d2e;
|
--input-background-color: #1f1d2e;
|
||||||
--input-border-color: #eb6f92;
|
--input-border-color: #eb6f92;
|
||||||
--input-border-size: 1.3px;
|
--input-border-size: 1.3px;
|
||||||
--navbar-logo-filter: none;
|
--navbar-logo-filter: none;
|
||||||
--dropdown-option-hover-color: #312a49;
|
--dropdown-option-hover-color: #312a49;
|
||||||
}
|
}
|
||||||
|
|
||||||
.font-inter {
|
.font-inter {
|
||||||
font-family: "Inter", sans-serif;
|
font-family: "Inter", sans-serif;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
.font-roboto {
|
.font-roboto {
|
||||||
font-family: "Roboto";
|
font-family: "Roboto";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,176 +1,177 @@
|
||||||
export function RammerheadEncode(baseUrl) { // Hellhead
|
export function RammerheadEncode(baseUrl) {
|
||||||
const mod = (n, m) => ((n % m) + m) % m;
|
// Hellhead
|
||||||
const baseDictionary =
|
const mod = (n, m) => ((n % m) + m) % m;
|
||||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~-";
|
const baseDictionary =
|
||||||
const shuffledIndicator = "_rhs";
|
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~-";
|
||||||
const generateDictionary = function () {
|
const shuffledIndicator = "_rhs";
|
||||||
let str = "";
|
const generateDictionary = function () {
|
||||||
const split = baseDictionary.split("");
|
let str = "";
|
||||||
while (split.length > 0) {
|
const split = baseDictionary.split("");
|
||||||
str += split.splice(Math.floor(Math.random() * split.length), 1)[0];
|
while (split.length > 0) {
|
||||||
}
|
str += split.splice(Math.floor(Math.random() * split.length), 1)[0];
|
||||||
return str;
|
}
|
||||||
};
|
return str;
|
||||||
interface StrShuffler {
|
};
|
||||||
dictionary: any;
|
interface StrShuffler {
|
||||||
}
|
dictionary: any;
|
||||||
class StrShuffler {
|
}
|
||||||
constructor(dictionary = generateDictionary()) {
|
class StrShuffler {
|
||||||
this.dictionary = dictionary;
|
constructor(dictionary = generateDictionary()) {
|
||||||
}
|
this.dictionary = dictionary;
|
||||||
shuffle(str) {
|
}
|
||||||
if (str.startsWith(shuffledIndicator)) {
|
shuffle(str) {
|
||||||
return str;
|
if (str.startsWith(shuffledIndicator)) {
|
||||||
}
|
return str;
|
||||||
let shuffledStr = "";
|
}
|
||||||
for (let i = 0; i < str.length; i++) {
|
let shuffledStr = "";
|
||||||
const char = str.charAt(i);
|
for (let i = 0; i < str.length; i++) {
|
||||||
const idx = baseDictionary.indexOf(char);
|
const char = str.charAt(i);
|
||||||
if (char === "%" && str.length - i >= 3) {
|
const idx = baseDictionary.indexOf(char);
|
||||||
shuffledStr += char;
|
if (char === "%" && str.length - i >= 3) {
|
||||||
shuffledStr += str.charAt(++i);
|
shuffledStr += char;
|
||||||
shuffledStr += str.charAt(++i);
|
shuffledStr += str.charAt(++i);
|
||||||
} else if (idx === -1) {
|
shuffledStr += str.charAt(++i);
|
||||||
shuffledStr += char;
|
} else if (idx === -1) {
|
||||||
} else {
|
shuffledStr += char;
|
||||||
shuffledStr += this.dictionary.charAt(
|
} else {
|
||||||
mod(idx + i, baseDictionary.length)
|
shuffledStr += this.dictionary.charAt(
|
||||||
);
|
mod(idx + i, baseDictionary.length)
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
return shuffledIndicator + shuffledStr;
|
}
|
||||||
}
|
return shuffledIndicator + shuffledStr;
|
||||||
unshuffle(str) {
|
}
|
||||||
if (!str.startsWith(shuffledIndicator)) {
|
unshuffle(str) {
|
||||||
return str;
|
if (!str.startsWith(shuffledIndicator)) {
|
||||||
}
|
return str;
|
||||||
|
}
|
||||||
str = str.slice(shuffledIndicator.length);
|
|
||||||
|
str = str.slice(shuffledIndicator.length);
|
||||||
let unshuffledStr = "";
|
|
||||||
for (let i = 0; i < str.length; i++) {
|
let unshuffledStr = "";
|
||||||
const char = str.charAt(i);
|
for (let i = 0; i < str.length; i++) {
|
||||||
const idx = this.dictionary.indexOf(char);
|
const char = str.charAt(i);
|
||||||
if (char === "%" && str.length - i >= 3) {
|
const idx = this.dictionary.indexOf(char);
|
||||||
unshuffledStr += char;
|
if (char === "%" && str.length - i >= 3) {
|
||||||
unshuffledStr += str.charAt(++i);
|
unshuffledStr += char;
|
||||||
unshuffledStr += str.charAt(++i);
|
unshuffledStr += str.charAt(++i);
|
||||||
} else if (idx === -1) {
|
unshuffledStr += str.charAt(++i);
|
||||||
unshuffledStr += char;
|
} else if (idx === -1) {
|
||||||
} else {
|
unshuffledStr += char;
|
||||||
unshuffledStr += baseDictionary.charAt(
|
} else {
|
||||||
mod(idx - i, baseDictionary.length)
|
unshuffledStr += baseDictionary.charAt(
|
||||||
);
|
mod(idx - i, baseDictionary.length)
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
return unshuffledStr;
|
}
|
||||||
}
|
return unshuffledStr;
|
||||||
}
|
}
|
||||||
function get(url, callback, shush = false) {
|
}
|
||||||
var request = new XMLHttpRequest();
|
function get(url, callback, shush = false) {
|
||||||
request.open("GET", url, true);
|
var request = new XMLHttpRequest();
|
||||||
request.send();
|
request.open("GET", url, true);
|
||||||
|
request.send();
|
||||||
request.onerror = function () {
|
|
||||||
if (!shush) console.log("Cannot communicate with the server");
|
request.onerror = function () {
|
||||||
};
|
if (!shush) console.log("Cannot communicate with the server");
|
||||||
request.onload = function () {
|
};
|
||||||
if (request.status === 200) {
|
request.onload = function () {
|
||||||
callback(request.responseText);
|
if (request.status === 200) {
|
||||||
} else {
|
callback(request.responseText);
|
||||||
if (!shush)
|
} else {
|
||||||
console.log(
|
if (!shush)
|
||||||
'unexpected server response to not match "200". Server says "' +
|
console.log(
|
||||||
request.responseText +
|
'unexpected server response to not match "200". Server says "' +
|
||||||
'"'
|
request.responseText +
|
||||||
);
|
'"'
|
||||||
}
|
);
|
||||||
};
|
}
|
||||||
}
|
};
|
||||||
var api = {
|
}
|
||||||
newsession(callback) {
|
var api = {
|
||||||
get("/newsession", callback);
|
newsession(callback) {
|
||||||
},
|
get("/newsession", callback);
|
||||||
sessionexists(id, callback) {
|
},
|
||||||
get("/sessionexists?id=" + encodeURIComponent(id), function (res) {
|
sessionexists(id, callback) {
|
||||||
if (res === "exists") return callback(true);
|
get("/sessionexists?id=" + encodeURIComponent(id), function (res) {
|
||||||
if (res === "not found") return callback(false);
|
if (res === "exists") return callback(true);
|
||||||
console.log("unexpected response from server. received" + res);
|
if (res === "not found") return callback(false);
|
||||||
});
|
console.log("unexpected response from server. received" + res);
|
||||||
},
|
});
|
||||||
shuffleDict(id, callback) {
|
},
|
||||||
console.log("Shuffling", id);
|
shuffleDict(id, callback) {
|
||||||
get("/api/shuffleDict?id=" + encodeURIComponent(id), function (res) {
|
console.log("Shuffling", id);
|
||||||
callback(JSON.parse(res));
|
get("/api/shuffleDict?id=" + encodeURIComponent(id), function (res) {
|
||||||
});
|
callback(JSON.parse(res));
|
||||||
}
|
});
|
||||||
};
|
}
|
||||||
var localStorageKey = "rammerhead_sessionids";
|
};
|
||||||
var localStorageKeyDefault = "rammerhead_default_sessionid";
|
var localStorageKey = "rammerhead_sessionids";
|
||||||
var sessionIdsStore = {
|
var localStorageKeyDefault = "rammerhead_default_sessionid";
|
||||||
get() {
|
var sessionIdsStore = {
|
||||||
var rawData = localStorage.getItem(localStorageKey);
|
get() {
|
||||||
if (!rawData) return [];
|
var rawData = localStorage.getItem(localStorageKey);
|
||||||
try {
|
if (!rawData) return [];
|
||||||
var data = JSON.parse(rawData);
|
try {
|
||||||
if (!Array.isArray(data)) throw "getout";
|
var data = JSON.parse(rawData);
|
||||||
return data;
|
if (!Array.isArray(data)) throw "getout";
|
||||||
} catch (e) {
|
return data;
|
||||||
return [];
|
} catch (e) {
|
||||||
}
|
return [];
|
||||||
},
|
}
|
||||||
set(data) {
|
},
|
||||||
if (!data || !Array.isArray(data)) throw new TypeError("must be array");
|
set(data) {
|
||||||
localStorage.setItem(localStorageKey, JSON.stringify(data));
|
if (!data || !Array.isArray(data)) throw new TypeError("must be array");
|
||||||
},
|
localStorage.setItem(localStorageKey, JSON.stringify(data));
|
||||||
getDefault() {
|
},
|
||||||
var sessionId = localStorage.getItem(localStorageKeyDefault);
|
getDefault() {
|
||||||
if (sessionId) {
|
var sessionId = localStorage.getItem(localStorageKeyDefault);
|
||||||
var data = sessionIdsStore.get();
|
if (sessionId) {
|
||||||
data.filter(function (e) {
|
var data = sessionIdsStore.get();
|
||||||
return e.id === sessionId;
|
data.filter(function (e) {
|
||||||
});
|
return e.id === sessionId;
|
||||||
if (data.length) return data[0];
|
});
|
||||||
}
|
if (data.length) return data[0];
|
||||||
return null;
|
}
|
||||||
},
|
return null;
|
||||||
setDefault(id) {
|
},
|
||||||
localStorage.setItem(localStorageKeyDefault, id);
|
setDefault(id) {
|
||||||
}
|
localStorage.setItem(localStorageKeyDefault, id);
|
||||||
};
|
}
|
||||||
function addSession(id) {
|
};
|
||||||
var data = sessionIdsStore.get();
|
function addSession(id) {
|
||||||
data.unshift({ id: id, createdOn: new Date().toLocaleString() });
|
var data = sessionIdsStore.get();
|
||||||
sessionIdsStore.set(data);
|
data.unshift({ id: id, createdOn: new Date().toLocaleString() });
|
||||||
}
|
sessionIdsStore.set(data);
|
||||||
function getSessionId() {
|
}
|
||||||
return new Promise((resolve) => {
|
function getSessionId() {
|
||||||
var id = localStorage.getItem("session-string");
|
return new Promise((resolve) => {
|
||||||
api.sessionexists(id, function (value) {
|
var id = localStorage.getItem("session-string");
|
||||||
if (!value) {
|
api.sessionexists(id, function (value) {
|
||||||
console.log("Session validation failed");
|
if (!value) {
|
||||||
api.newsession(function (id) {
|
console.log("Session validation failed");
|
||||||
addSession(id);
|
api.newsession(function (id) {
|
||||||
localStorage.setItem("session-string", id);
|
addSession(id);
|
||||||
console.log(id);
|
localStorage.setItem("session-string", id);
|
||||||
console.log("^ new id");
|
console.log(id);
|
||||||
resolve(id);
|
console.log("^ new id");
|
||||||
});
|
resolve(id);
|
||||||
} else {
|
});
|
||||||
resolve(id);
|
} else {
|
||||||
}
|
resolve(id);
|
||||||
});
|
}
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
var ProxyHref;
|
}
|
||||||
|
var ProxyHref;
|
||||||
return getSessionId().then((id) => {
|
|
||||||
return new Promise((resolve, reject) => {
|
return getSessionId().then((id) => {
|
||||||
api.shuffleDict(id, function (shuffleDict) {
|
return new Promise((resolve, reject) => {
|
||||||
var shuffler = new StrShuffler(shuffleDict);
|
api.shuffleDict(id, function (shuffleDict) {
|
||||||
ProxyHref = "/" + id + "/" + shuffler.shuffle(baseUrl);
|
var shuffler = new StrShuffler(shuffleDict);
|
||||||
resolve(ProxyHref);
|
ProxyHref = "/" + id + "/" + shuffler.shuffle(baseUrl);
|
||||||
});
|
resolve(ProxyHref);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
export function searchUtil(input: string, template: string) {
|
export function searchUtil(input: string, template: string) {
|
||||||
try {
|
try {
|
||||||
return new URL(input).toString();
|
return new URL(input).toString();
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
try {
|
try {
|
||||||
const url = new URL(`http://${input}`);
|
const url = new URL(`http://${input}`);
|
||||||
if (url.hostname.includes(".")) return url.toString();
|
if (url.hostname.includes(".")) return url.toString();
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
return template.replace("%s", encodeURIComponent(input));
|
return template.replace("%s", encodeURIComponent(input));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,20 @@
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
export default {
|
export default {
|
||||||
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
||||||
theme: {
|
theme: {
|
||||||
colors: {
|
colors: {
|
||||||
primary: "var(--background-primary)",
|
primary: "var(--background-primary)",
|
||||||
lighter: "var(--background-lighter)",
|
lighter: "var(--background-lighter)",
|
||||||
"navbar-text-color": "var(--navbar-text-color)",
|
"navbar-text-color": "var(--navbar-text-color)",
|
||||||
"navbar-color": "var(--navbar-color)",
|
"navbar-color": "var(--navbar-color)",
|
||||||
"text-color": "var(--navbar-link-color)",
|
"text-color": "var(--navbar-link-color)",
|
||||||
"text-hover-color": "var(--navbar-link-hover-color)",
|
"text-hover-color": "var(--navbar-link-hover-color)",
|
||||||
input: "var(--input-background-color)",
|
input: "var(--input-background-color)",
|
||||||
"input-text": "var(--input-text-color)",
|
"input-text": "var(--input-text-color)",
|
||||||
"input-border-color": "var(--input-border-color)",
|
"input-border-color": "var(--input-border-color)",
|
||||||
"dropdown-option-hover-color": "var(--dropdown-option-hover-color)",
|
"dropdown-option-hover-color": "var(--dropdown-option-hover-color)"
|
||||||
},
|
},
|
||||||
extend: {}
|
extend: {}
|
||||||
},
|
},
|
||||||
plugins: []
|
plugins: []
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2020",
|
"target": "ES2020",
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"jsxImportSource": "preact",
|
"jsxImportSource": "preact",
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"paths": {}
|
"paths": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,31 @@
|
||||||
import million from "million/compiler";
|
import million from "million/compiler";
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import preact from "@preact/preset-vite";
|
import preact from "@preact/preset-vite";
|
||||||
import { viteStaticCopy } from "vite-plugin-static-copy";
|
import { viteStaticCopy } from "vite-plugin-static-copy";
|
||||||
import { uvPath } from "@titaniumnetwork-dev/ultraviolet";
|
import { uvPath } from "@titaniumnetwork-dev/ultraviolet";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
viteStaticCopy({
|
viteStaticCopy({
|
||||||
targets: [
|
targets: [
|
||||||
{
|
{
|
||||||
// .replace fixes weird paths on Windows
|
// .replace fixes weird paths on Windows
|
||||||
src: `${uvPath}/uv.*.js`.replace(/\\/g, "/"),
|
src: `${uvPath}/uv.*.js`.replace(/\\/g, "/"),
|
||||||
dest: "uv",
|
dest: "uv",
|
||||||
overwrite: false
|
overwrite: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
million.vite({ auto: true }),
|
million.vite({ auto: true }),
|
||||||
preact()
|
preact()
|
||||||
],
|
],
|
||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
"/bare": {
|
"/bare": {
|
||||||
target: "http://localhost:8080/",
|
target: "http://localhost:8080/",
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path) => path.replace(/^\/bare/, "")
|
rewrite: (path) => path.replace(/^\/bare/, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue