mirror of https://github.com/VERT-sh/VERT.git
feat: homepage
This commit is contained in:
parent
70a17df7bf
commit
0370ff3abf
|
@ -12,6 +12,7 @@
|
||||||
"lint": "prettier --check . && eslint ."
|
"lint": "prettier --check . && eslint ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@poppanator/sveltekit-svg": "^5.0.0",
|
||||||
"@sveltejs/adapter-auto": "^3.0.0",
|
"@sveltejs/adapter-auto": "^3.0.0",
|
||||||
"@sveltejs/kit": "^2.0.0",
|
"@sveltejs/kit": "^2.0.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import "@poppanator/sveltekit-svg/dist/svg";
|
||||||
|
|
||||||
type EventPayload = {
|
type EventPayload = {
|
||||||
readonly n: string;
|
readonly n: string;
|
||||||
readonly u: Location["href"];
|
readonly u: Location["href"];
|
||||||
|
|
|
@ -4,6 +4,12 @@
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Host+Grotesk:ital,wght@0,300..800;1,300..800&family=Radio+Canada+Big:ital,wght@0,400..700;1,400..700&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
<body data-sveltekit-preload-data="hover">
|
<body data-sveltekit-preload-data="hover">
|
||||||
|
|
55
src/app.scss
55
src/app.scss
|
@ -7,10 +7,10 @@
|
||||||
@import url(@fontsource/azeret-mono/600.css);
|
@import url(@fontsource/azeret-mono/600.css);
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--font-body: "Lexend", system-ui, -apple-system, BlinkMacSystemFont,
|
--font-body: "Host Grotesk", system-ui, -apple-system, BlinkMacSystemFont,
|
||||||
"Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans",
|
"Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans",
|
||||||
"Helvetica Neue", sans-serif;
|
"Helvetica Neue", sans-serif;
|
||||||
--font-display: "Azeret Mono", var(--font-body);
|
--font-display: "Radio Canada Big", var(--font-body);
|
||||||
--transition: linear(
|
--transition: linear(
|
||||||
0,
|
0,
|
||||||
0.006,
|
0.006,
|
||||||
|
@ -27,6 +27,7 @@
|
||||||
1.017 63.9%,
|
1.017 63.9%,
|
||||||
1.001
|
1.001
|
||||||
);
|
);
|
||||||
|
--shadow-panel: 0 4px 6px 0 hsla(0, 0%, 0%, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin light {
|
@mixin light {
|
||||||
|
@ -43,19 +44,35 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin dark {
|
@mixin dark {
|
||||||
--accent-bg: hsl(304, 41%, 21%);
|
// general
|
||||||
--accent-fg: hsl(303, 73%, 81%);
|
--accent: hsl(303, 73%, 81%);
|
||||||
--bg: hsl(0, 0%, 8%);
|
|
||||||
--bg-transparent: hsla(0, 0%, 8%, 0.8);
|
// foregrounds
|
||||||
--fg: hsl(0, 0%, 90%);
|
--fg: hsl(0, 0%, 100%);
|
||||||
--fg-muted: hsl(0, 0%, 50%);
|
--fg-muted: hsl(0, 0%, 50%);
|
||||||
--fg-muted-alt: hsl(0, 0%, 25%);
|
--fg-on-accent: hsl(0, 0%, 0%);
|
||||||
--fg-highlight: hsl(303, 64%, 65%);
|
|
||||||
--fg-failure: hsl(0, 67%, 80%);
|
// backgrounds
|
||||||
--bg-gradient: linear-gradient(to bottom, #c800ff0c, #c800ff00),
|
--bg: hsl(220, 5%, 12%);
|
||||||
linear-gradient(to bottom left, #0015ff0c, #0015ff00 50%),
|
--bg-gradient: linear-gradient(
|
||||||
linear-gradient(to bottom right, #ff001e18, #ff001e00 50%);
|
to bottom,
|
||||||
|
hsla(287, 100%, 50%, 0.1),
|
||||||
|
hsla(287, 100%, 50%, 0)
|
||||||
|
),
|
||||||
|
linear-gradient(
|
||||||
|
to bottom left,
|
||||||
|
hsla(235, 100%, 50%, 0.07),
|
||||||
|
hsla(235, 100%, 50%, 0) 50%
|
||||||
|
),
|
||||||
|
linear-gradient(
|
||||||
|
to bottom right,
|
||||||
|
hsla(353, 100%, 50%, 0.07),
|
||||||
|
hsla(353, 100%, 50%, 0) 50%
|
||||||
|
);
|
||||||
color-scheme: dark;
|
color-scheme: dark;
|
||||||
|
--bg-panel: hsl(225, 4%, 18%);
|
||||||
|
--bg-panel-accented: color-mix(in srgb, var(--accent) 12%, transparent);
|
||||||
|
--bg-separator: hsl(214, 4%, 32%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
|
@ -79,9 +96,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@apply text-foreground bg-background font-body overflow-x-hidden;
|
@apply text-foreground font-body font-semibold overflow-x-hidden;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
background: var(--bg-gradient);
|
background: var(--bg-gradient);
|
||||||
|
background-color: var(--bg);
|
||||||
background-size: 100vw 100vh;
|
background-size: 100vw 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,4 +119,13 @@ body {
|
||||||
.btn-highlight {
|
.btn-highlight {
|
||||||
@apply bg-accent-background text-accent-foreground border-accent-background;
|
@apply bg-accent-background text-accent-foreground border-accent-background;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
@apply font-display font-semibold;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<svg width="1389" height="1080" viewBox="0 0 1389 1080" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M418.719 1080L0.480804 0H2.62554L420.863 1080H418.719Z" fill="url(#paint0_linear_6_220)" fill-opacity="0.1"/>
|
||||||
|
<path d="M829.044 1080L412.359 0H410.215L826.9 1080H829.044Z" fill="url(#paint1_linear_6_220)" fill-opacity="0.1"/>
|
||||||
|
<path d="M788.673 555.925L987.856 0H989.981L790.985 555.402L1064.61 827.169L1386.13 0H1388.27L1065.37 830.741L788.673 555.925Z" fill="url(#paint2_linear_6_220)" fill-opacity="0.1"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_6_220" x1="694.377" y1="0" x2="694.377" y2="1080" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="white"/>
|
||||||
|
<stop offset="0.75" stop-color="white"/>
|
||||||
|
<stop offset="1" stop-color="white" stop-opacity="0"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_6_220" x1="694.377" y1="0" x2="694.377" y2="1080" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="white"/>
|
||||||
|
<stop offset="0.75" stop-color="white"/>
|
||||||
|
<stop offset="1" stop-color="white" stop-opacity="0"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint2_linear_6_220" x1="694.377" y1="0" x2="694.377" y2="1080" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="white"/>
|
||||||
|
<stop offset="0.75" stop-color="white"/>
|
||||||
|
<stop offset="1" stop-color="white" stop-opacity="0"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,54 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { onMount, type SvelteComponentTyped } from "svelte";
|
||||||
|
import Panel from "../visual/Panel.svelte";
|
||||||
|
import Logo from "../visual/svg/Logo.svelte";
|
||||||
|
import clsx from "clsx";
|
||||||
|
import { page } from "$app/stores";
|
||||||
|
import { MoonIcon, SunIcon } from "lucide-svelte";
|
||||||
|
import { theme } from "$lib/store/index.svelte";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
items: {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
activeMatch: (pathname: string) => boolean;
|
||||||
|
icon: any;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
let { items }: Props = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Panel class="w-fit h-20 flex items-center gap-3">
|
||||||
|
<div
|
||||||
|
class="w-32 h-full bg-accent rounded-xl flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<div class="h-5 w-full">
|
||||||
|
<Logo />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{#each items as item (item.url)}
|
||||||
|
{@const Icon = item.icon}
|
||||||
|
<a
|
||||||
|
href={item.url}
|
||||||
|
aria-label={item.name}
|
||||||
|
class={clsx(
|
||||||
|
"w-32 h-full rounded-xl flex items-center justify-center gap-3",
|
||||||
|
{
|
||||||
|
"bg-panel-accented": item.activeMatch($page.url.pathname),
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Icon />
|
||||||
|
<p>{item.name}</p>
|
||||||
|
</a>
|
||||||
|
{/each}
|
||||||
|
<div class="w-0.5 bg-separator h-full"></div>
|
||||||
|
<button
|
||||||
|
onclick={theme.toggle}
|
||||||
|
class="w-14 h-full flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<SunIcon class="dynadark:hidden block" />
|
||||||
|
<MoonIcon class="dynadark:block hidden" />
|
||||||
|
</button>
|
||||||
|
</Panel>
|
|
@ -1,121 +1,23 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Upload } from "lucide-svelte";
|
import { UploadIcon } from "lucide-svelte";
|
||||||
import clsx from "clsx";
|
import Panel from "../visual/Panel.svelte";
|
||||||
import { onMount } from "svelte";
|
|
||||||
|
|
||||||
let fileList = $state<FileList>();
|
type Props = {
|
||||||
let dragBtn = $state<HTMLButtonElement>();
|
class?: string;
|
||||||
|
};
|
||||||
|
|
||||||
interface Props {
|
const { class: classList }: Props = $props();
|
||||||
files: File[] | undefined;
|
|
||||||
onupload?: () => void;
|
|
||||||
isMobile: boolean;
|
|
||||||
acceptedFormats?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
if (!fileList) return;
|
|
||||||
files = Array.from(fileList);
|
|
||||||
});
|
|
||||||
|
|
||||||
let fileInput = $state<HTMLInputElement>();
|
|
||||||
let dragOver = $state(false);
|
|
||||||
|
|
||||||
let { files = $bindable(), onupload, isMobile, acceptedFormats }: Props = $props();
|
|
||||||
|
|
||||||
function upload() {
|
|
||||||
if (!fileInput) return;
|
|
||||||
fileInput.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
const handler = (e: Event) => e.preventDefault();
|
|
||||||
if (!dragBtn) return;
|
|
||||||
dragBtn.addEventListener("dragenter", handler);
|
|
||||||
dragBtn.addEventListener("dragstart", handler);
|
|
||||||
dragBtn.addEventListener("dragend", handler);
|
|
||||||
dragBtn.addEventListener("dragleave", handler);
|
|
||||||
dragBtn.addEventListener("dragover", handler);
|
|
||||||
dragBtn.addEventListener("drag", handler);
|
|
||||||
dragBtn.addEventListener("drop", handler);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
if (!dragBtn) return;
|
|
||||||
dragBtn.removeEventListener("dragenter", handler);
|
|
||||||
dragBtn.removeEventListener("dragstart", handler);
|
|
||||||
dragBtn.removeEventListener("dragend", handler);
|
|
||||||
dragBtn.removeEventListener("dragleave", handler);
|
|
||||||
dragBtn.removeEventListener("dragover", handler);
|
|
||||||
dragBtn.removeEventListener("drag", handler);
|
|
||||||
dragBtn.removeEventListener("drop", handler);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
function drop(event: DragEvent) {
|
|
||||||
event.preventDefault();
|
|
||||||
dragOver = false;
|
|
||||||
if (!event.dataTransfer) return;
|
|
||||||
if (!files) files = Array.from(event.dataTransfer.files);
|
|
||||||
else files.push(...Array.from(event.dataTransfer.files));
|
|
||||||
onupload?.();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function addFiles() {
|
|
||||||
if (!fileInput) return;
|
|
||||||
if (!fileInput.files) return;
|
|
||||||
if (!files) files = Array.from(fileInput.files);
|
|
||||||
else files.push(...Array.from(fileInput.files));
|
|
||||||
onupload?.();
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button
|
<button class={classList}>
|
||||||
bind:this={dragBtn}
|
<Panel class="flex justify-center items-center w-full h-full flex-col">
|
||||||
onclick={upload}
|
|
||||||
ondragover={() => (dragOver = true)}
|
|
||||||
ondragleave={() => (dragOver = false)}
|
|
||||||
class={clsx(
|
|
||||||
"file-uploader",
|
|
||||||
"w-full h-80 flex items-center justify-center cursor-pointer",
|
|
||||||
"border-2 border-solid border-foreground-muted-alt rounded-2xl",
|
|
||||||
"hover:scale-95 hover:opacity-70 transition-all duration-150 ease-out",
|
|
||||||
{
|
|
||||||
"scale-95 opacity-70 blur-xs": dragOver,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
class:_drag-over={dragOver}
|
|
||||||
ondrop={drop}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="file-uploader-center flex items-center justify-center flex-col transition-all duration-150 ease-out px-8"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="size-16 rounded-full text-accent-foreground bg-accent-background flex items-center justify-center"
|
class="w-16 h-16 bg-accent rounded-full flex items-center justify-center p-4"
|
||||||
>
|
>
|
||||||
<Upload class="size-8" />
|
<UploadIcon class="w-full h-full text-on-accent" />
|
||||||
</div>
|
</div>
|
||||||
<h2 class="font-display text-2xl mt-6">
|
<h2 class="text-center text-2xl font-semibold mt-4">
|
||||||
{isMobile ? "Tap" : "Drop or click"} to upload files
|
Drop or click to upload
|
||||||
</h2>
|
</h2>
|
||||||
<p class="text-foreground-muted mt-4">
|
</Panel>
|
||||||
All processing is done on your device. No file or size limit.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
class="hidden"
|
|
||||||
bind:this={fileInput}
|
|
||||||
onchange={addFiles}
|
|
||||||
accept={acceptedFormats?.join(",") ?? "*"}
|
|
||||||
multiple
|
|
||||||
/>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.file-uploader:hover .file-uploader-center,
|
|
||||||
.file-uploader._drag-over .file-uploader-center {
|
|
||||||
@apply scale-105;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<script lang="ts">
|
||||||
|
type Props = {
|
||||||
|
class: string;
|
||||||
|
items: { [name: string]: string };
|
||||||
|
};
|
||||||
|
|
||||||
|
const { class: classList, items }: Props = $props();
|
||||||
|
|
||||||
|
const year = new Date().getFullYear();
|
||||||
|
|
||||||
|
const links = $derived(Object.entries(items));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<footer class={classList}>
|
||||||
|
<div
|
||||||
|
class="w-full h-full flex items-center justify-center text-muted gap-3"
|
||||||
|
>
|
||||||
|
<p>© {year} VERT.</p>
|
||||||
|
{#each links as [name, url] (name)}
|
||||||
|
<!-- bullet point -->
|
||||||
|
<p>•</p>
|
||||||
|
<a target="_blank" class="hover:underline" href={url}>{name}</a>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</footer>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { Snippet } from "svelte";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
class?: string;
|
||||||
|
children: Snippet<[]>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const { class: classList, children }: Props = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="bg-panel {classList} p-3 rounded-2.5xl shadow-panel">
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
|
@ -8,20 +8,42 @@ class Files {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Theme {
|
class Theme {
|
||||||
public dark = $state(false);
|
private _dark = $state(false);
|
||||||
public toggle = () => {
|
public get dark() {
|
||||||
this.dark = !this.dark;
|
return this._dark;
|
||||||
|
}
|
||||||
|
public set dark(value: boolean) {
|
||||||
|
this._dark = value;
|
||||||
|
if (!browser) return;
|
||||||
JSCookie.set("theme", this.dark ? "dark" : "light", {
|
JSCookie.set("theme", this.dark ? "dark" : "light", {
|
||||||
path: "/",
|
path: "/",
|
||||||
sameSite: "lax",
|
sameSite: "lax",
|
||||||
expires: 2147483647,
|
expires: 2147483647,
|
||||||
});
|
});
|
||||||
log(["theme"], `set to ${this.dark ? "dark" : "light"}`);
|
log(["theme"], `set to ${this.dark ? "dark" : "light"}`);
|
||||||
if (browser) {
|
window.plausible("Theme set", {
|
||||||
window.plausible("Theme set", {
|
props: { theme: theme.dark ? "dark" : "light" },
|
||||||
props: { theme: theme.dark ? "dark" : "light" },
|
});
|
||||||
|
if (value) {
|
||||||
|
document.documentElement.classList.add("dark");
|
||||||
|
document.documentElement.classList.remove("light");
|
||||||
|
JSCookie.set("theme", "dark", {
|
||||||
|
path: "/",
|
||||||
|
sameSite: "lax",
|
||||||
|
expires: 2147483647,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
document.documentElement.classList.add("light");
|
||||||
|
document.documentElement.classList.remove("dark");
|
||||||
|
JSCookie.set("theme", "light", {
|
||||||
|
path: "/",
|
||||||
|
sameSite: "lax",
|
||||||
|
expires: 2147483647,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
public toggle = () => {
|
||||||
|
this.dark = !this.dark;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,27 +13,39 @@
|
||||||
} from "$env/static/public";
|
} from "$env/static/public";
|
||||||
import FancyMenu from "$lib/components/functional/FancyMenu.svelte";
|
import FancyMenu from "$lib/components/functional/FancyMenu.svelte";
|
||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
import { MoonIcon, SunIcon } from "lucide-svelte";
|
import {
|
||||||
|
InfoIcon,
|
||||||
|
MoonIcon,
|
||||||
|
RefreshCwIcon,
|
||||||
|
SettingsIcon,
|
||||||
|
SunIcon,
|
||||||
|
UploadIcon,
|
||||||
|
} from "lucide-svelte";
|
||||||
import { browser } from "$app/environment";
|
import { browser } from "$app/environment";
|
||||||
import JSCookie from "js-cookie";
|
import JSCookie from "js-cookie";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
|
import Panel from "$lib/components/visual/Panel.svelte";
|
||||||
|
import Navbar from "$lib/components/functional/Navbar.svelte";
|
||||||
|
import Footer from "$lib/components/visual/Footer.svelte";
|
||||||
let { children, data } = $props();
|
let { children, data } = $props();
|
||||||
|
|
||||||
let shouldGoBack = writable(false);
|
let shouldGoBack = writable(false);
|
||||||
let navbar = $state<HTMLDivElement>();
|
let navbar = $state<HTMLDivElement>();
|
||||||
let hover = $state(false);
|
let hover = $state(false);
|
||||||
|
|
||||||
const links = $derived<
|
const items = $derived<
|
||||||
{
|
{
|
||||||
name: string;
|
name: string;
|
||||||
url: string;
|
url: string;
|
||||||
activeMatch: (pathname: string) => boolean;
|
activeMatch: (pathname: string) => boolean;
|
||||||
|
icon: any;
|
||||||
}[]
|
}[]
|
||||||
>([
|
>([
|
||||||
{
|
{
|
||||||
name: "Upload",
|
name: "Upload",
|
||||||
url: "/",
|
url: "/",
|
||||||
activeMatch: (pathname) => pathname === "/",
|
activeMatch: (pathname) => pathname === "/",
|
||||||
|
icon: UploadIcon,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name:
|
name:
|
||||||
|
@ -42,11 +54,19 @@
|
||||||
: `Convert`,
|
: `Convert`,
|
||||||
url: "/convert",
|
url: "/convert",
|
||||||
activeMatch: (pathname) => pathname === "/convert",
|
activeMatch: (pathname) => pathname === "/convert",
|
||||||
|
icon: RefreshCwIcon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Settings",
|
||||||
|
url: "/settings",
|
||||||
|
activeMatch: (pathname) => pathname.startsWith("/settings"),
|
||||||
|
icon: SettingsIcon,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "About",
|
name: "About",
|
||||||
url: "/about",
|
url: "/about",
|
||||||
activeMatch: (pathname) => pathname.startsWith("/about"),
|
activeMatch: (pathname) => pathname.startsWith("/about"),
|
||||||
|
icon: InfoIcon,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -57,27 +77,6 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
if (!browser) return;
|
|
||||||
if (theme.dark) {
|
|
||||||
document.documentElement.classList.add("dark");
|
|
||||||
document.documentElement.classList.remove("light");
|
|
||||||
JSCookie.set("theme", "dark", {
|
|
||||||
path: "/",
|
|
||||||
sameSite: "lax",
|
|
||||||
expires: 2147483647,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
document.documentElement.classList.add("light");
|
|
||||||
document.documentElement.classList.remove("dark");
|
|
||||||
JSCookie.set("theme", "light", {
|
|
||||||
path: "/",
|
|
||||||
sameSite: "lax",
|
|
||||||
expires: 2147483647,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const mouseEnter = () => {
|
const mouseEnter = () => {
|
||||||
hover = true;
|
hover = true;
|
||||||
|
@ -104,182 +103,21 @@
|
||||||
></script>{/if}
|
></script>{/if}
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<div
|
<div class="fixed top-8 left-0 w-full flex justify-center">
|
||||||
role="main"
|
<Navbar {items} />
|
||||||
class="w-full h-full max-w-screen-lg mx-auto p-4"
|
</div>
|
||||||
ondragenter={maybeNavToHome}
|
|
||||||
>
|
<div class="w-screen h-screen">
|
||||||
<div class="flex justify-center mb-5 lg:hidden">
|
{@render children()}
|
||||||
<a
|
</div>
|
||||||
href="/"
|
|
||||||
class="px-4 relative h-14 mr-3 justify-center items-center bg-accent-background fill-accent-foreground rounded-xl md:hidden flex"
|
<div class="-mt-14 w-full h-14">
|
||||||
>
|
<Footer
|
||||||
<div class="h-6 relative w-24 items-center flex justify-center">
|
class="w-full h-full"
|
||||||
<Logo />
|
items={{
|
||||||
{#if PUB_ENV === "nightly"}
|
"Privacy Policy": "#",
|
||||||
<div
|
"Source Code": "#",
|
||||||
class="absolute -top-6 -left-10 px-2 py-1 w-fit bg-foreground-highlight text-accent-background rotate-[-10deg] rounded-xl"
|
"Discord Server": "#",
|
||||||
style="font-family: Comic Sans MS, sans-serif;"
|
}}
|
||||||
>
|
/>
|
||||||
NIGHTLY
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="w-full max-w-screen-md p-1 border-solid border-2 rounded-2xl border-foreground-muted-alt flex mb-10 mx-auto lg:mt-5"
|
|
||||||
bind:this={navbar}
|
|
||||||
>
|
|
||||||
<div class="md:p-1">
|
|
||||||
<a
|
|
||||||
href="/"
|
|
||||||
class="px-3 relative w-full h-full mr-3 justify-center items-center bg-accent-background fill-accent-foreground rounded-xl md:flex hidden"
|
|
||||||
>
|
|
||||||
<div class="h-6 w-24 items-center flex justify-center relative">
|
|
||||||
<Logo />
|
|
||||||
{#if PUB_ENV === "nightly"}
|
|
||||||
<div
|
|
||||||
class="absolute -top-6 -left-10 px-2 py-1 w-fit bg-foreground-highlight text-accent-background rotate-[-10deg] rounded-xl"
|
|
||||||
style="font-family: Comic Sans MS, sans-serif;"
|
|
||||||
>
|
|
||||||
NIGHTLY
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<FancyMenu {links} {shouldGoBack} />
|
|
||||||
<div class="h-16 px-4 flex items-center">
|
|
||||||
<button onclick={theme.toggle} class="grid-cols-1 grid-rows-1 grid">
|
|
||||||
<!-- {#if theme.dark}
|
|
||||||
<div
|
|
||||||
class="w-full h-full flex items-center justify-center row-start-1 col-start-1"
|
|
||||||
>
|
|
||||||
<MoonIcon />
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<div
|
|
||||||
class="w-full h-full flex items-center justify-center row-start-1 col-start-1"
|
|
||||||
>
|
|
||||||
<SunIcon />
|
|
||||||
</div>
|
|
||||||
{/if} -->
|
|
||||||
{#if browser}
|
|
||||||
{#if theme.dark}
|
|
||||||
<div
|
|
||||||
in:blur={{
|
|
||||||
blurMultiplier: 1,
|
|
||||||
duration,
|
|
||||||
easing: quintOut,
|
|
||||||
scale: {
|
|
||||||
start: 0.5,
|
|
||||||
end: 1,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
out:blur={{
|
|
||||||
blurMultiplier: 1,
|
|
||||||
duration,
|
|
||||||
easing: quintOut,
|
|
||||||
scale: {
|
|
||||||
start: 1,
|
|
||||||
end: 1.5,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
class="w-full h-full flex items-center justify-center row-start-1 col-start-1"
|
|
||||||
>
|
|
||||||
<MoonIcon class="w-8" />
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<div
|
|
||||||
in:blur={{
|
|
||||||
blurMultiplier: 1,
|
|
||||||
duration,
|
|
||||||
easing: quintOut,
|
|
||||||
scale: {
|
|
||||||
start: 0.5,
|
|
||||||
end: 1,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
out:blur={{
|
|
||||||
blurMultiplier: 1,
|
|
||||||
duration,
|
|
||||||
easing: quintOut,
|
|
||||||
scale: {
|
|
||||||
start: 1,
|
|
||||||
end: 1.5,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
class="w-full h-full flex items-center justify-center row-start-1 col-start-1"
|
|
||||||
>
|
|
||||||
<SunIcon class="w-8" />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{:else}
|
|
||||||
<div
|
|
||||||
class="w-full h-full flex items-center justify-center row-start-1 col-start-1 dynadark:hidden"
|
|
||||||
>
|
|
||||||
<SunIcon class="w-8" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="w-full h-full hidden items-center justify-center row-start-1 col-start-1 dynadark:flex"
|
|
||||||
>
|
|
||||||
<MoonIcon class="w-8" />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="w-full max-w-screen-lg grid grid-cols-1 grid-rows-1 relative">
|
|
||||||
{#key data.pathname}
|
|
||||||
<div class="w-full">
|
|
||||||
<div
|
|
||||||
class="absolute top-0 left-0 w-full"
|
|
||||||
style={hover ? "will-change: opacity, blur, transform" : ""}
|
|
||||||
in:blur={{
|
|
||||||
duration,
|
|
||||||
easing: quintOut,
|
|
||||||
blurMultiplier: 12,
|
|
||||||
x: {
|
|
||||||
start: !$shouldGoBack ? 250 : -250,
|
|
||||||
end: 0,
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
start: 100,
|
|
||||||
end: 0,
|
|
||||||
},
|
|
||||||
scale: {
|
|
||||||
start: 0.75,
|
|
||||||
end: 1,
|
|
||||||
},
|
|
||||||
origin: "top center",
|
|
||||||
}}
|
|
||||||
out:blur={{
|
|
||||||
duration,
|
|
||||||
easing: quintOut,
|
|
||||||
blurMultiplier: 12,
|
|
||||||
x: {
|
|
||||||
start: 0,
|
|
||||||
end: !$shouldGoBack ? -250 : 250,
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
start: 0,
|
|
||||||
end: 100,
|
|
||||||
},
|
|
||||||
scale: {
|
|
||||||
start: 1,
|
|
||||||
end: 0.75,
|
|
||||||
},
|
|
||||||
origin: "top center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div class="pb-20">
|
|
||||||
{@render children()}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/key}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,4 +5,32 @@
|
||||||
//
|
//
|
||||||
// ship fast n break things !!
|
// ship fast n break things !!
|
||||||
// -- nullptr
|
// -- nullptr
|
||||||
|
|
||||||
|
import VertVBig from "$lib/assets/vert-bg.svg?component";
|
||||||
|
import Uploader from "$lib/components/functional/Uploader.svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="fixed -z-50 top-0 left-0 w-full h-full flex items-center justify-center overflow-hidden"
|
||||||
|
>
|
||||||
|
<VertVBig class="fill-[--fg] opacity-50" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full h-full flex items-center justify-center">
|
||||||
|
<div class="max-w-5xl w-full">
|
||||||
|
<div class="flex items-center h-[266px] gap-24">
|
||||||
|
<div class="flex-grow w-full">
|
||||||
|
<h1 class="text-6xl tracking-tight leading-[72px] mb-6">
|
||||||
|
The file converter you'll love.
|
||||||
|
</h1>
|
||||||
|
<p class="font-normal text-xl text-muted">
|
||||||
|
All processing done on your device. No file size limit, no
|
||||||
|
ads, and completely open source.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow w-full h-full">
|
||||||
|
<Uploader class="w-full h-full" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
|
@ -5,15 +5,21 @@ export default {
|
||||||
content: ["./src/**/*.{html,js,svelte,ts}"],
|
content: ["./src/**/*.{html,js,svelte,ts}"],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
backgroundColor: {
|
||||||
background: "var(--bg)",
|
panel: "var(--bg-panel)",
|
||||||
|
"panel-accented": "var(--bg-panel-accented)",
|
||||||
|
separator: "var(--bg-separator)",
|
||||||
|
},
|
||||||
|
textColor: {
|
||||||
foreground: "var(--fg)",
|
foreground: "var(--fg)",
|
||||||
"foreground-muted": "var(--fg-muted)",
|
muted: "var(--fg-muted)",
|
||||||
"foreground-muted-alt": "var(--fg-muted-alt)",
|
"on-accent": "var(--fg-on-accent)",
|
||||||
"foreground-failure": "var(--fg-failure)",
|
},
|
||||||
"foreground-highlight": "var(--fg-highlight)",
|
colors: {
|
||||||
"accent-background": "var(--accent-bg)",
|
accent: "var(--accent)",
|
||||||
"accent-foreground": "var(--accent-fg)",
|
},
|
||||||
|
boxShadow: {
|
||||||
|
panel: "var(--shadow-panel)",
|
||||||
},
|
},
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
display: "var(--font-display)",
|
display: "var(--font-display)",
|
||||||
|
@ -22,6 +28,9 @@ export default {
|
||||||
blur: {
|
blur: {
|
||||||
xs: "2px",
|
xs: "2px",
|
||||||
},
|
},
|
||||||
|
borderRadius: {
|
||||||
|
"2.5xl": "1.25rem",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { sveltekit } from "@sveltejs/kit/vite";
|
import { sveltekit } from "@sveltejs/kit/vite";
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
|
import svg from "@poppanator/sveltekit-svg";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -17,6 +18,19 @@ export default defineConfig({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
svg({
|
||||||
|
includePaths: ["./src/lib/assets"],
|
||||||
|
svgoOptions: {
|
||||||
|
multipass: true,
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
name: "preset-default",
|
||||||
|
params: { overrides: { removeViewBox: false } },
|
||||||
|
},
|
||||||
|
{ name: "removeAttrs", params: { attrs: "(fill|stroke)" } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
exclude: [
|
exclude: [
|
||||||
|
|
Loading…
Reference in New Issue