Marketplace: change the way we deal with themes (no refresh)

This commit is contained in:
MotorTruck1221 2024-10-12 01:17:41 -06:00
parent 6b227f241b
commit c63662f602
No known key found for this signature in database
GPG key ID: 08F417E2B8B61EA4
17 changed files with 352 additions and 392 deletions

View file

@ -1,19 +1,19 @@
:root {
--background-primary: rgba(0, 0, 0, 0) !important;
--background-lighter: #000 !important;
--navbar-color: #000 !important;
--navbar-height: 60px !important;
--navbar-text-color: greenyellow !important;
--navbar-link-color: greenyellow !important;
--navbar-link-hover-color: green !important;
--navbar-font: "Roboto" !important;
--input-text-color: greenyellow !important;
--input-placeholder-color: white !important;
--input-background-color: #000 !important;
--input-border-color: greenyellow !important;
--input-border-size: 1.3px !important;
--navbar-logo-filter: none !important;
--dropdown-option-hover-color: #312a49 !important;
--tab-color: var(--black) !important;
--border-color: greenyellow !important;
--background-primary: rgba(0, 0, 0, 0) ;
--background-lighter: #000 ;
--navbar-color: #000 ;
--navbar-height: 60px ;
--navbar-text-color: greenyellow ;
--navbar-link-color: greenyellow ;
--navbar-link-hover-color: green ;
--navbar-font: "Roboto" ;
--input-text-color: greenyellow ;
--input-placeholder-color: white ;
--input-background-color: #000 ;
--input-border-color: greenyellow ;
--input-border-size: 1.3px ;
--navbar-logo-filter: none ;
--dropdown-option-hover-color: #312a49 ;
--tab-color: var(--black) ;
--border-color: greenyellow ;
}

View file

@ -26,6 +26,7 @@
"@mercuryworkshop/libcurl-transport": "^1.3.10",
"@rubynetwork/rammerhead": "^1.3.5",
"@rubynetwork/rammerhead-browser": "^1.0.9",
"@svelte-drama/suspense": "0.5.1",
"@titaniumnetwork-dev/ultraviolet": "3.1.2",
"astro": "^4.15.11",
"astro-icon": "^1.1.1",

12
pnpm-lock.yaml generated
View file

@ -44,6 +44,9 @@ importers:
'@rubynetwork/rammerhead-browser':
specifier: ^1.0.9
version: 1.0.9
'@svelte-drama/suspense':
specifier: 0.5.1
version: 0.5.1(svelte@4.2.19)
'@titaniumnetwork-dev/ultraviolet':
specifier: 3.1.2
version: 3.1.2
@ -829,6 +832,11 @@ packages:
'@shikijs/vscode-textmate@9.3.0':
resolution: {integrity: sha512-jn7/7ky30idSkd/O5yDBfAnVt+JJpepofP/POZ1iMOxK59cOfqIgg/Dj0eFsjOTMw+4ycJN0uhZH/Eb0bs/EUA==}
'@svelte-drama/suspense@0.5.1':
resolution: {integrity: sha512-fovSQHYDxb9fEnMVBHxBi4v39CR+Hrsp16lSYOHm+aPfm8FyBQtG8VsrIg9BszNWz8GCEFIVCaE/Iqty1ovB6Q==}
peerDependencies:
svelte: ^3.54.0 || ^4.0.0 || ^5.0.0
'@sveltejs/vite-plugin-svelte-inspector@2.1.0':
resolution: {integrity: sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==}
engines: {node: ^18.0.0 || >=20}
@ -4603,6 +4611,10 @@ snapshots:
'@shikijs/vscode-textmate@9.3.0': {}
'@svelte-drama/suspense@0.5.1(svelte@4.2.19)':
dependencies:
svelte: 4.2.19
'@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.8(@types/node@22.7.5)))(svelte@4.2.19)(vite@5.4.8(@types/node@22.7.5))':
dependencies:
'@sveltejs/vite-plugin-svelte': 3.1.2(svelte@4.2.19)(vite@5.4.8(@types/node@22.7.5))

View file

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

View file

@ -1,41 +1,33 @@
<script>
<script lang="ts">
import { Settings } from "@utils/settings";
import { Suspense } from "@svelte-drama/suspense";
export let page;
let assetPromise = get_assets();
async function get_assets() {
async function getAssets() {
const response = await fetch("/api/catalog-assets?page=" + page);
const data = await response.json();
return data.assets;
}
const assets = getAssets();
</script>
<div class="text-3xl roboto font-bold text-text-color p-10">
{#await assetPromise}
Loading assets...
{:then assets}
{#if Object.keys(assets).length > 0}
<Suspense let:suspend>
<div slot="loading">
<p class="text-4xl"> Loading... </p>
</div>
{#await suspend(assets) then data}
{#if Object.keys(data).length > 0}
<div class="flex flex-row gap-6 flex-wrap justify-center">
{#each Object.entries(assets) as [key, asset]}
<a href={"/assets/" + key}>
<div
class="bg-navbar-color w-64 rounded-3xl shadow-lg overflow-hidden transition-transform duration-300 hover:scale-105"
>
<img
src={"/images/" + asset.image}
alt={asset.title}
class="w-full h-40 object-cover"
/>
{#each Object.entries(data) as [key, asset]}
<a href={`/assets/${key}`}>
<div class="bg-navbar-color w-64 rounded-3xl shadow-lg overflow-hidden transition-transform duration-300 hover:scale-105">
<img src={`/images/${asset.image}`} alt={asset.title} class="w-full h-40 object-cover" />
<div class="p-6 text-sm">
<div class="font-semibold text-2xl mb-2">
{asset.title}
</div>
<div class="mb-4">{asset.description}</div>
<div class="flex flex-wrap gap-2 mb-4">
<p class="font-semibold text-2xl mb-2"> {asset.title} </p>
<p class="mb-4"> {asset.description} </p>
<div class="flex flex-wrap gap-2 mb-4 w-full">
{#each asset.tags as tag}
<div
class="bg-navbar-text-color text-navbar-color font-bold px-3 py-1 rounded-md text-center"
>
{tag}
</div>
<p class="bg-navbar-text-color text-navbar-color font-bold px-3 py-1 rounded-md text-center"> {tag} </p>
{/each}
</div>
<div>
@ -48,16 +40,7 @@ async function get_assets() {
</a>
{/each}
</div>
{:else}
<div class="text-2xl text-center text-text-color mt-6 w-96">
No assets available at the moment. The server host's database is empty.
Please contact the maintainer of this site if you think this is an
error.
</div>
{/if}
{:catch someError}
<div class="text-lg text-center text-red-500 mt-6">
System error: {someError.message}.
</div>
{/await}
</Suspense>
</div>

View file

@ -1,11 +1,11 @@
<script>
let assetPromise = get_all_assets();
async function logItem(item) {
// hell
<script lang="ts">
import { Suspense } from "@svelte-drama/suspense";
import { Settings, marketPlaceSettings } from '@utils/settings';
import Parent from "./Parent.svelte";
async function getItem(item) {
try {
const response = await fetch(`/api/packages/${item}`);
const data = await response.json();
return {
...data,
package_name: item
@ -14,131 +14,51 @@ async function logItem(item) {
console.error("error: failed to fetch", error);
return null;
}
}
async function get_all_assets() {
let items = JSON.parse(localStorage.getItem("installed_themes")) || [];
const promises = items.map(logItem);
}
async function getAssets() {
const items = JSON.parse(localStorage.getItem(Settings.AppearanceSettings.themes)) || [];
const promises = items.map(getItem);
const dataArray = await Promise.all(promises);
const accumulatedData = dataArray.filter((data) => data !== null);
console.log(JSON.stringify(accumulatedData));
return accumulatedData;
}
function install(assets_json, package_name) {
if (assets_json.background_video) {
localStorage.setItem("background_video", assets_json.background_video);
} else {
localStorage.removeItem("video");
}
if (assets_json.background_image) {
localStorage.setItem("background_image", assets_json.background_image);
} else {
localStorage.removeItem("background_image");
}
if (assets_json.type == "theme") {
localStorage.setItem("stylesheet", "/styles/" + assets_json.payload);
}
location.reload();
}
function reset_theme() {
localStorage.removeItem("background_video");
localStorage.removeItem("background_image");
localStorage.removeItem("stylesheet");
location.reload();
}
function delete_theme(key) {
let items = JSON.parse(localStorage.getItem("installed_themes")) || [];
const index = items.indexOf(key);
items.splice(index, 1);
localStorage.setItem("installed_themes", JSON.stringify(items));
reset_theme();
}
let assets = getAssets();
let compRef = [];
</script>
{#await assetPromise}
Loading assets...
{:then assets}
<!-- svelte-ignore a11y-no-static-element-interactions -->
{#each Object.entries(assets) as [key, asset]}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<Suspense let:suspend>
<div class="rounded-3xl bg-navbar-color w-64 flex flex-col cursor-pointer">
<div class="w-full" on:click={() => install(asset, key)}>
<img
src={"/images/" + asset.image}
alt="Theme"
class="aspect-[16/9] rounded-t-3xl"
/>
<div class="w-full" on:click={() => {marketPlaceSettings.changeTheme(true)}}>
<img src='/classic_theme.png' alt="Classic Nebula" class="aspect-[16/9] rounded-t-3xl"/>
</div>
<div
class="h-2/6 text-center content-center p-3 font-semibold items-center flex flex-col"
>
<div class="text-2xl">{asset.title}</div>
<div class="h-2/6 text-center content-center p-3 font-semibold items-center flex flex-col text-2xl">
Classic Nebula
</div>
</div>
{#await suspend(assets) then data}
{#each Object.entries(data) as [key, asset]}
<Parent bind:this={compRef[key]}>
<div class="rounded-3xl bg-navbar-color w-64 flex flex-col cursor-pointer">
<div class="w-full" on:click={() => {marketPlaceSettings.changeTheme(false, asset.payload, asset.background_video, asset.background_image)}}>
<img src={`/images/${asset.image}`} alt="theme" class="aspect-[16/9] rounded-t-3xl"/>
</div>
<div class="h-2/6 text-center content-center p-3 font-semibold items-center flex flex-col">
<div class="text-2xl"> {asset.title} </div>
<div class="flex flex-row">
<div
class="h-8 w-8 cursor-pointer"
on:click={() => delete_theme(key)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="100%"
height="100%"
viewBox="0 0 256 256"
{...$$props}
>
<path
fill="currentColor"
d="M216 48h-40v-8a24 24 0 0 0-24-24h-48a24 24 0 0 0-24 24v8H40a8 8 0 0 0 0 16h8v144a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16V64h8a8 8 0 0 0 0-16M112 168a8 8 0 0 1-16 0v-64a8 8 0 0 1 16 0Zm48 0a8 8 0 0 1-16 0v-64a8 8 0 0 1 16 0Zm0-120H96v-8a8 8 0 0 1 8-8h48a8 8 0 0 1 8 8Z"
/>
<div class="h-8 w-8 cursor-pointer" on:click={() => {marketPlaceSettings.uninstall("theme", asset.package_name); marketPlaceSettings.changeTheme(true); compRef[key].$destroy()}}>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 256 256" {...$$props}>
<path fill="currentColor" d="M216 48h-40v-8a24 24 0 0 0-24-24h-48a24 24 0 0 0-24 24v8H40a8 8 0 0 0 0 16h8v144a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16V64h8a8 8 0 0 0 0-16M112 168a8 8 0 0 1-16 0v-64a8 8 0 0 1 16 0Zm48 0a8 8 0 0 1-16 0v-64a8 8 0 0 1 16 0Zm0-120H96v-8a8 8 0 0 1 8-8h48a8 8 0 0 1 8 8Z" />
</svg>
</div>
<a
class="h-8 w-8 cursor-pointer"
href={"/assets/" + asset.package_name}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="100%"
height="100%"
viewBox="0 0 256 256"
{...$$props}
>
<path
fill="currentColor"
d="M192 136v72a16 16 0 0 1-16 16H48a16 16 0 0 1-16-16V80a16 16 0 0 1 16-16h72a8 8 0 0 1 0 16H48v128h128v-72a8 8 0 0 1 16 0m32-96a8 8 0 0 0-8-8h-64a8 8 0 0 0-5.66 13.66L172.69 72l-42.35 42.34a8 8 0 0 0 11.32 11.32L184 83.31l26.34 26.35A8 8 0 0 0 224 104Z"
/>
<a class="h-8 w-8 cursor-pointer" href={"/assets/" + asset.package_name}>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 256 256" {...$$props}>
<path fill="currentColor" d="M192 136v72a16 16 0 0 1-16 16H48a16 16 0 0 1-16-16V80a16 16 0 0 1 16-16h72a8 8 0 0 1 0 16H48v128h128v-72a8 8 0 0 1 16 0m32-96a8 8 0 0 0-8-8h-64a8 8 0 0 0-5.66 13.66L172.69 72l-42.35 42.34a8 8 0 0 0 11.32 11.32L184 83.31l26.34 26.35A8 8 0 0 0 224 104Z" />
</svg>
</a>
</div>
</div>
</div>
</Parent>
{/each}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class="rounded-3xl bg-navbar-color w-64 flex flex-col"
on:click={() => reset_theme()}
>
<div class="w-full">
<img
src="/classic_theme.png"
alt="Theme"
class="aspect-[16/9] rounded-t-3xl"
/>
</div>
<div class="h-2/6 text-center content-center p-3 font-semibold text-2xl">
Classic Nebula
</div>
</div>
{:catch someError}
System error: {someError.message}.
{/await}
{/await}
</Suspense>

View file

@ -0,0 +1 @@
<slot />

View file

@ -1,5 +1,5 @@
<script>
import { tabSettings, proxySettings, Settings } from "@utils/settings.ts";
import { tabSettings, proxySettings, marketPlaceSettings, Settings } from "@utils/settings.ts";
// This loads the settings in a nice way
tabSettings.cloakTab(localStorage.getItem(Settings.TabSettings.tabCloak) as string || "default");
proxySettings.changeProxy(localStorage.getItem(Settings.ProxySettings.proxy) as string || "automatic");
@ -7,7 +7,9 @@
proxySettings.setSearchEngine(localStorage.getItem(Settings.ProxySettings.searchEngine) as string || "ddg");
proxySettings.setWispURL(localStorage.getItem(Settings.ProxySettings.wispServerURL) as string || "default");
proxySettings.setTransport(localStorage.getItem(Settings.ProxySettings.transport) as string || "epoxy");
marketPlaceSettings.changeTheme(false, undefined, localStorage.getItem(Settings.AppearanceSettings.video) as string, localStorage.getItem(Settings.AppearanceSettings.image) as string);
document.addEventListener("astro:after-swap", function() {
tabSettings.cloakTab(localStorage.getItem(Settings.TabSettings.tabCloak) as string || "default");
//marketPlaceSettings.changeTheme(false);
});
</script>

View file

@ -77,30 +77,6 @@ const { title, noHeader } = Astro.props;
}
}
});
const stylesheet_link = localStorage.getItem("stylesheet");
const stylesheet = document.getElementById(
"stylesheet"
)! as HTMLAnchorElement;
if (stylesheet_link) {
stylesheet.href = stylesheet_link;
}
const video_link = localStorage.getItem("background_video");
const image_link = localStorage.getItem("background_image");
const image_source = document.getElementById("nebulaImage")! as any;
const source = document.getElementById("nebulaVideo")! as any;
if (video_link) {
source.src = "/videos/" + video_link;
}
if (image_link) {
image_source.style.display = "block";
image_source.src = "/images/" + image_link;
}
</script>
<style>
#mobileNavMenu {

View file

@ -8,32 +8,16 @@ import { Icon } from "astro-icon/components";
---
<div class="flex flex-row">
<div
class="text-text-color mt-16 fixed inset-0 h-[calc(100%-4rem)] z-0 bg-primary flex-col flex md:flex-row"
>
<div
class="items-center p-3 flex flex-row border-border-color gap-5 border-r-2 md:w-2/12 md:flex-col md:bg-navbar-color md:gap-0 md:p-0"
>
<SidebarButton
title={t("settings.appearance")}
route={`/${lang}/settings/appearance`}
>
<Icon
name="ph:palette"
class="h-6 w-6 text-text-color transition duration-500 group-hover:text-text-hover-color md:h-6 md:w-6"
/>
<div class="text-text-color mt-16 fixed inset-0 h-[calc(100%-4rem)] z-0 bg-primary flex-col flex md:flex-row">
<div class="items-center p-3 flex flex-row border-border-color gap-5 border-r-2 md:w-2/12 md:flex-col md:bg-navbar-color md:gap-0 md:p-0">
<SidebarButton title={t("settings.appearance")} route={`/${lang}/settings/appearance`}>
<Icon name="ph:palette" class="h-6 w-6 text-text-color transition duration-500 group-hover:text-text-hover-color md:h-6 md:w-6" />
</SidebarButton>
<SidebarButton title={t("settings.proxy")} route={`/${lang}/settings/pr`}>
<Icon
name="ph:globe-hemisphere-east-fill"
class="h-6 w-6 text-text-color transition duration-500 group-hover:text-text-hover-color md:h-6 md:w-6"
/>
<Icon name="ph:globe-hemisphere-east-fill" class="h-6 w-6 text-text-color transition duration-500 group-hover:text-text-hover-color md:h-6 md:w-6" />
</SidebarButton>
<SidebarButton title={t("settings.tab")} route={`/${lang}/settings/tab/`}>
<Icon
name="ph:laptop-fill"
class="h-6 w-6 text-text-color transition duration-500 group-hover:text-text-hover-color md:h-6 md:w-6"
/>
<Icon name="ph:laptop-fill" class="h-6 w-6 text-text-color transition duration-500 group-hover:text-text-hover-color md:h-6 md:w-6" />
</SidebarButton>
</div>
<div class="p-8 md:w-10/12 overflow-y-auto">

View file

@ -19,7 +19,7 @@ export const prerender = true;
if (iframe) {
initSw().then(() => {
setTransport(localStorage.getItem(Settings.ProxySettings.transport) as string).then(async () => {
iframe.src = __uv$config!.prefix + __uv$config.encodeUrl("https://radon.games");
iframe.src = __uv$config!.prefix + __uv$config.encodeUrl!("https://radon.games");
});
});
}

View file

@ -11,7 +11,7 @@ const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
---
<Layout title="Welcome to Astro.">
<Layout title="Nebula">
<div
class="flex flex-wrap mt-16 justify-center content-center w-full bg-primary fixed inset-0 h-[calc(100%-4rem)] z-0 flex-col items-center"
>

View file

@ -1,12 +1,9 @@
---
import InstalledThemes from "@components/catalog/InstalledThemes.svelte";
import ThemeCard from "@components/settings/ThemeCard.astro";
import Layout from "@layouts/Layout.astro";
import SettingsLayout from "@layouts/SettingsLayout.astro";
import SettingsSection from "@layouts/SettingsSection.astro";
import { Icon } from "astro-icon/components";
import ClassicNebula from "../../../assets/classic_theme.png";
import fortnite from "../../../assets/fortnite.jpg";
import { getLangFromUrl, useTranslations } from "../../../i18n/utils";
const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
@ -25,14 +22,14 @@ export const prerender = true;
<div class="flex flex-row flex-wrap gap-4 items-center roboto">
<div class="justify-center flex flex-row gap-6 flex-wrap md:justify-normal">
<InstalledThemes client:only="svelte" />
<div class="rounded-3xl bg-navbar-color w-64 flex flex-col">
<a href="/catalog/1" class="rounded-3xl bg-navbar-color w-64 flex flex-col">
<div class="w-full items-center justify-center flex aspect-[16/9]">
<Icon name="ph:plus-bold" class="h-16 w-16" />
</div>
<div class="h-2/6 text-center content-center p-3 font-semibold">
Get more themes in the <strong>Nebula Catalog!</strong>
</div>
</div>
</a>
</div>
<div class="text-3xl roboto font-bold text-text-color"></div>
</div>

View file

@ -0,0 +1,80 @@
---
const { packageName } = Astro.params;
import Layout from "@layouts/Layout.astro";
const response = await fetch(new URL("/api/packages/" + packageName, Astro.url));
const assetsJson = await response.json();
---
<Layout title={`Package: ${packageName}`}>
<div class="flex flex-wrap mt-16 w-full fixed inset-0 h-full md:h-[calc(100%-4rem)] z-0 bg-primary flex-col items-center content-center justify-center md:pb-64 roboto max-md:p-4">
{assetsJson.error && <h1 class="text-text-color text-3xl font-bold"> Unexpected error. Is the name right? </h1>}
{!assetsJson.error &&
<div class="flex flex-col md:flex-row items-center text-text-color bg-navbar-color rounded-2xl">
{assetsJson.background_video &&
<video src={`/videos/${assetsJson.background_video}`} controls class="w-[44rem] h-[25rem] object-cover rounded-xl">
Your browser does not support the video tag.
</video>
}
{assetsJson.backgroundImage &&
<div style={{backgroundImage: `url(/images/${assetsJson.backgroundImage})`}} class="w-[44rem] h-[25rem] bg-cover bg-center rounded-xl"/>
}
{!assetsJson.background_video && !assetsJson.backgroundImage && <img src={`/images/${assetsJson.image}`} alt={assetsJson .title} class="w-[44rem] h-[25rem] object-cover rounded-xl"/>}
<div class="flex flex-col ml-7 p-16">
<p class="text-xl">{assetsJson.type}</p>
<h1 class="text-4xl roboto font-semibold">{assetsJson.title}</h1>
<p class="text-xl"> By: <strong>{assetsJson.author}</strong></p>
<p class="text-xl">{assetsJson.description}</p>
<button class="bg-primary text-text-color border border-transparent rounded-lg px-6 py-3 hover:bg-navbar-color transition-colors duration-300 mt-9" id="install">
Install
</button>
<button class="hidden bg-primary text-text-color border border-transparent rounded-lg px-6 py-3 hover:bg-navbar-color transition-colors duration-300 mt-9" id="uninstall">
Uninstall
</button>
</div>
</div>
}
</div>
<variable-define data-assets={JSON.stringify(assetsJson)} data-name={packageName} data-type={assetsJson.type} />
</Layout>
<script>
import { Settings, type PackageType, marketPlaceSettings } from "@utils/settings";
let packageName: string;
let assetsJson: any;
let packageType: string;
//some weird trickery to get the variables.
class VariableDefiner extends HTMLElement {
constructor() {
super();
assetsJson = this.dataset.assets;
packageName = this.dataset.name as string;
packageType = this.dataset.type as PackageType;
}
}
customElements.define('variable-define', VariableDefiner);
document.addEventListener('astro:page-load', () => {
try {
const items = JSON.parse(localStorage.getItem(Settings.AppearanceSettings.themes) as string) || [];
const itemExists = items.indexOf(packageName) !== -1;
const installButton = document.getElementById("install") as HTMLButtonElement;
const uninstallButton = document.getElementById("uninstall") as HTMLButtonElement;
const payload = assetsJson ? JSON.parse(assetsJson) : undefined;
if (itemExists) {
uninstallButton.classList.remove("hidden");
installButton.classList.add("hidden");
}
installButton.addEventListener("click", () => {
marketPlaceSettings.install({theme: {payload: payload.payload, video: payload.background_video, bgImage: payload.background_image}}, packageName, payload.payload).then(() => {
installButton.classList.add("hidden");
uninstallButton.classList.remove("hidden");
})
});
uninstallButton.addEventListener("click", () => {
marketPlaceSettings.uninstall(packageType as PackageType, packageName).then(() => {
uninstallButton.classList.add("hidden")
installButton.classList.remove("hidden");
});
})
}
catch (err) { /* DEBUGGING ONLY: console.error(err) */ }
});
</script>

View file

@ -1,103 +0,0 @@
---
const { package_name } = Astro.params;
import Layout from "@layouts/Layout.astro";
const response = await fetch(new URL("/api/packages/" + package_name, Astro.url));
const assetsJson = await response.json();
---
<Layout title="Package">
<div class="flex flex-wrap mt-16 w-full fixed inset-0 h-[calc(100%-4rem)] z-0 bg-primary flex-col items-center content-center justify-center pb-64 roboto">
{assetsJson.error ? (
<h1 class="text-text-color text-3xl"> Unexpected error. Are you sure you typed the name right? </h1>
) : (
<div class="flex flex-row items-center text-text-color bg-navbar-color rounded-2xl">
{assetsJson.background_video ? (
// Background video
<video src={`/videos/${assetsJson.background_video}`} controls class="w-[44rem] h-[25rem] object-cover rounded-xl">
Your browser does not support the video tag.
</video>
) : assetsJson.backgroundImage ? (
// Background image
<div style={{backgroundImage: `url(/images/${assetsJson.backgroundImage})`}} class="w-[44rem] h-[25rem] bg-cover bg-center rounded-xl"/>
) : (
// Fallback to cover image
<img src={`/images/${assetsJson.image}`} alt={assetsJson .title} class="w-[44rem] h-[25rem] object-cover rounded-xl"/>
)}
<div class="flex flex-col ml-7 p-16">
<div class="text-xl">{assetsJson.type}</div>
<div class="text-4xl roboto font-semibold">{assetsJson.title}</div>
<div class="text-xl">
By <strong>{assetsJson.author}</strong>
</div>
<div class="text-xl">{assetsJson.description}</div>
<button class="bg-primary text-text-color border border-transparent rounded-lg px-6 py-3 hover:bg-navbar-color transition-colors duration-300 mt-9" id="install">
Install
</button>
</div>
</div>
)
}
</div>
<script is:inline define:vars={{ assetsJson, package_name }}>
// determine if this is installed
let items = JSON.parse(localStorage.getItem("installed_themes")) || [];
const packageExists = items.indexOf(package_name) !== -1;
if (packageExists) {
document.getElementById("install").textContent = "Uninstall";
}
document.getElementById("install").addEventListener("click", install);
function install() {
if (packageExists) {
const index = items.indexOf(package_name);
items.splice(index, 1);
localStorage.setItem("installed_themes", JSON.stringify(items));
localStorage.removeItem("background_video");
localStorage.removeItem("background_image");
localStorage.removeItem("stylesheet");
location.reload();
} else {
console.log("Package does not exist in the items array.");
let installedThemes = localStorage.getItem("installed_themes");
if (installedThemes) {
// If it exists, append it
installedThemes = JSON.parse(installedThemes);
installedThemes.push(package_name);
} else {
// If it doesn't exist, create a new array
installedThemes = [package_name];
}
localStorage.setItem(
"installed_themes",
JSON.stringify(installedThemes)
);
if (assetsJson.background_video) {
localStorage.setItem(
"background_video",
assetsJson.background_video
);
} else {
localStorage.removeItem("video");
}
if (assetsJson.background_image) {
localStorage.setItem(
"background_image",
assetsJson.background_image
);
} else {
localStorage.removeItem("background_image");
}
if (assetsJson.type == "theme") {
localStorage.setItem("stylesheet", "/styles/" + assetsJson.payload);
}
location.reload();
}
}
</script>
</Layout>

View file

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

View file

@ -1,4 +1,10 @@
const wispUrl = (location.protocol === "https:" ? "wss://" : "ws://") + location.host + "/wisp/";
const AppearanceSettings = {
themes: "nebula||themes",
stylePayload: "nebula||stylepayload",
video: "nebula||video",
image: "nebula||image"
}
const TabSettings = {
tabCloak: "nebula||tabCloak",
abblob: "nebula||abBlob"
@ -11,6 +17,7 @@ const ProxySettings = {
transport: "nebula||transport"
};
const Settings = {
AppearanceSettings,
TabSettings,
ProxySettings
};
@ -20,6 +27,15 @@ type AbCloaks = "a:b" | "blob";
type OpenIn = "a:b" | "blob" | "direct" | "embed";
type Proxy = "automatic" | "uv" | "rh";
type Transport = "epoxy" | "libcurl";
type PackageType = "theme" | "plugin"
interface Package {
theme?: {
payload: string,
video?: string
bgImage?: string
}
plugin?: {}
}
const SearchEngines: Record<string, string> = {
ddg: "https://duckduckgo.com/?q=%s",
google: "https://google.com/search?q=%s",
@ -125,4 +141,92 @@ const proxySettings = {
}
};
export { tabSettings, proxySettings, Settings, WispServerURLS, SearchEngines, cloak, type Proxy };
const marketPlaceSettings = {
install: function(p: Package, packageName: string, payload?: any) {
return new Promise<void>((resolve) => {
if (p.theme) {
let themes = localStorage.getItem(Settings.AppearanceSettings.themes) as any;
themes ? themes = JSON.parse(themes) : themes = [];
if (!themes.find((theme: any) => theme === packageName)) {
themes.push(packageName);
localStorage.setItem(Settings.AppearanceSettings.themes, JSON.stringify(themes));
this.changeTheme(false, payload, p.theme.video, p.theme.bgImage);
}
resolve();
}
});
},
uninstall: function(p: PackageType, packageName: string) {
return new Promise<void>((resolve) => {
if (p === "theme") {
let items = localStorage.getItem(Settings.AppearanceSettings.themes) as any;
items ? items = JSON.parse(items) : items = [];
if (items.find((theme: any) => theme === packageName)) {
const idx = items.indexOf(packageName);
items.splice(idx, 1);
localStorage.setItem(Settings.AppearanceSettings.themes, JSON.stringify(items));
this.changeTheme(true);
}
resolve();
}
});
},
changeTheme: async function(reset: Boolean, payload?: any, videoSource?: string, bgSource?: string) {
async function resetCSS() {
const stylesheet = document.getElementById("stylesheet")! as HTMLLinkElement;
localStorage.removeItem(Settings.AppearanceSettings.stylePayload);
stylesheet.href = "/nebula.css";
}
function resetVideo() {
localStorage.removeItem(Settings.AppearanceSettings.video);
const source = document.getElementById('nebulaVideo')! as HTMLVideoElement;
source.src = "";
}
function resetBGImage() {
localStorage.removeItem(Settings.AppearanceSettings.image);
const image = document.getElementById("nebulaImage")! as HTMLImageElement;
image.style.display = "none";
image.src = "";
}
if (reset === true) {
await resetCSS();
await resetCSS();
resetBGImage();
resetVideo();
}
if (videoSource || localStorage.getItem(Settings.AppearanceSettings.video)) {
resetBGImage();
resetVideo();
const source = document.getElementById("nebulaVideo")! as HTMLVideoElement;
if (!localStorage.getItem(Settings.AppearanceSettings.video)) {
localStorage.setItem(Settings.AppearanceSettings.video, videoSource as string);
}
source.src = `/videos/${videoSource ? videoSource : localStorage.getItem(Settings.AppearanceSettings.video)}`
}
if (bgSource || localStorage.getItem(Settings.AppearanceSettings.image)) {
resetVideo();
resetBGImage();
const image = document.getElementById("nebulaImage")! as HTMLImageElement;
if (!localStorage.getItem(Settings.AppearanceSettings.image)) {
localStorage.setItem(Settings.AppearanceSettings.image, bgSource as string);
}
image.style.display = "block";
image.src = `/images/${bgSource ? bgSource : localStorage.getItem(Settings.AppearanceSettings.image)}`
}
if (payload) {
const stylesheet = document.getElementById("stylesheet")! as HTMLLinkElement;
if (localStorage.getItem(Settings.AppearanceSettings.stylePayload) !== payload) {
localStorage.setItem(Settings.AppearanceSettings.stylePayload, payload);
}
stylesheet.href = `/styles/${localStorage.getItem(Settings.AppearanceSettings.stylePayload)}`;
}
else {
if (localStorage.getItem(Settings.AppearanceSettings.stylePayload)) {
const stylesheet = document.getElementById("stylesheet")! as HTMLLinkElement;
stylesheet.href = `/styles/${localStorage.getItem(Settings.AppearanceSettings.stylePayload)}`;
}
}
}
}
export { tabSettings, proxySettings, marketPlaceSettings, Settings, WispServerURLS, SearchEngines, cloak, type Proxy, type PackageType };