Merge branch 'nightly'

This commit is contained in:
JovannMC 2025-02-10 23:40:26 +03:00
commit 21faba31f1
No known key found for this signature in database
21 changed files with 433 additions and 326 deletions

View File

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { files } from "$lib/store/index.svelte"; import { effects, files } from "$lib/store/index.svelte";
import { FolderArchiveIcon, RefreshCw } from "lucide-svelte"; import { FolderArchiveIcon, RefreshCw } from "lucide-svelte";
import Panel from "../visual/Panel.svelte"; import Panel from "../visual/Panel.svelte";
import Dropdown from "./Dropdown.svelte"; import Dropdown from "./Dropdown.svelte";
@ -11,14 +11,14 @@
<div class="flex items-center flex-col md:flex-row gap-2.5 max-md:w-full"> <div class="flex items-center flex-col md:flex-row gap-2.5 max-md:w-full">
<button <button
onclick={() => files.convertAll()} onclick={() => files.convertAll()}
class="btn highlight flex gap-3 max-md:w-full" class="btn {$effects ? "" : "!scale-100"} highlight flex gap-3 max-md:w-full"
disabled={!files.ready} disabled={!files.ready}
> >
<RefreshCw size="24" /> <RefreshCw size="24" />
<p>Convert all</p> <p>Convert all</p>
</button> </button>
<button <button
class="btn flex gap-3 max-md:w-full" class="btn {$effects ? "" : "!scale-100"} flex gap-3 max-md:w-full"
disabled={!files.ready || !files.results} disabled={!files.ready || !files.results}
onclick={() => files.downloadAll()} onclick={() => files.downloadAll()}
> >

View File

@ -3,7 +3,7 @@
import Panel from "../visual/Panel.svelte"; import Panel from "../visual/Panel.svelte";
import clsx from "clsx"; import clsx from "clsx";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { files } from "$lib/store/index.svelte"; import { effects, files } from "$lib/store/index.svelte";
import { converters } from "$lib/converters"; import { converters } from "$lib/converters";
import { goto } from "$app/navigation"; import { goto } from "$app/navigation";
@ -61,7 +61,7 @@
<button <button
onclick={uploadFiles} onclick={uploadFiles}
bind:this={uploaderButton} bind:this={uploaderButton}
class={clsx(`hover:scale-105 active:scale-100 duration-200 ${classList}`)} class={clsx(`hover:scale-105 active:scale-100 ${$effects ? "" : "!scale-100"} duration-200 ${classList}`)}
> >
<Panel <Panel
class="flex justify-center items-center w-full h-full flex-col pointer-events-none" class="flex justify-center items-center w-full h-full flex-col pointer-events-none"

View File

@ -1,22 +1,23 @@
<script lang="ts"> <script lang="ts">
type Props = { import { GITHUB_URL_VERT, DISCORD_URL } from "$lib/consts";
class: string;
items: { [name: string]: string };
};
const { class: classList, items }: Props = $props(); const items = Object.entries({
//"Privacy policy": "#",
"Source code": GITHUB_URL_VERT,
"Discord server": DISCORD_URL,
});
const year = new Date().getFullYear(); const year = new Date().getFullYear();
const links = $derived(Object.entries(items));
</script> </script>
<footer class={classList}> <footer
class="hidden md:block w-full h-14 border-t border-separator fixed bottom-0 mt-12"
>
<div <div
class="w-full h-full flex items-center justify-center text-muted gap-3 relative" class="w-full h-full flex items-center justify-center text-muted gap-3 relative"
> >
<p>© {year} VERT.</p> <p>© {year} VERT.</p>
{#each links as [name, url] (name)} {#each items as [name, url] (name)}
<!-- bullet point --> <!-- bullet point -->
<p></p> <p></p>
<a <a

View File

@ -0,0 +1,93 @@
<script lang="ts">
import { page } from "$app/state";
import { duration } from "$lib/animation";
import VertVBig from "$lib/assets/vert-bg.svg?component";
import { files, gradientColor, showGradient } from "$lib/store/index.svelte";
import { quintOut } from "svelte/easing";
import { fade } from "$lib/animation";
</script>
{#if page.url.pathname === "/"}
<div
class="fixed -z-30 top-0 left-0 w-screen h-screen flex items-center justify-center overflow-hidden"
transition:fade={{
duration,
easing: quintOut,
}}
>
<VertVBig
class="fill-[--fg] opacity-10 dynadark:opacity-5 scale-[200%] md:scale-[80%]"
/>
</div>
<div
id="gradient-bg"
class="fixed top-0 left-0 w-screen h-screen -z-40 pointer-events-none"
style="background: var(--bg-gradient);"
transition:fade={{
duration,
easing: quintOut,
}}
></div>
{:else if page.url.pathname === "/convert" && $showGradient}
{#key $gradientColor}
<div
id="gradient-bg"
class="fixed top-0 left-0 w-screen h-screen -z-40 pointer-events-none"
style="background: var(--bg-gradient-{$gradientColor || 'pink'});"
transition:fade={{
duration,
easing: quintOut,
}}
></div>
{/key}
{:else if page.url.pathname === "/convert" && files.files.length === 1 && files.files[0].blobUrl}
<div
class="fixed w-screen h-screen opacity-75 overflow-hidden top-0 left-0 -z-50 pointer-events-none grid grid-cols-1 grid-rows-1 scale-105"
>
<div
class="w-full relative"
transition:fade={{
duration,
easing: quintOut,
}}
>
<img
class="object-cover w-full h-full blur-md"
src={files.files[0].blobUrl}
alt={files.files[0].name}
/>
<div
class="absolute top-0 left-0 w-full h-full"
style="background: var(--bg-gradient-image);"
></div>
<!-- <div class="absolute bottom-0 left-0 w-full h-full">
<ProgressiveBlur
direction="bottom"
endIntensity={256}
iterations={8}
fadeTo="var(--bg)"
/>
</div> -->
</div>
</div>
{:else if page.url.pathname === "/settings"}
<div
id="gradient-bg"
class="fixed top-0 left-0 w-screen h-screen -z-40 pointer-events-none"
style="background: var(--bg-gradient-blue);"
transition:fade={{
duration,
easing: quintOut,
}}
></div>
{:else if page.url.pathname === "/about"}
<div
id="gradient-bg"
class="fixed top-0 left-0 w-screen h-screen -z-40 pointer-events-none"
style="background: var(--bg-gradient-pink);"
transition:fade={{
duration,
easing: quintOut,
}}
></div>
{/if}

View File

@ -0,0 +1,18 @@
<script>
import Logo from "$lib/components/visual/svg/Logo.svelte";
</script>
<div class="flex md:hidden justify-center items-center pb-8 pt-4">
<a
class="flex items-center justify-center bg-panel p-2 rounded-[20px] shadow-panel"
href="/"
>
<div
class="h-14 bg-accent rounded-[14px] flex items-center justify-center"
>
<div class="w-28 h-5">
<Logo />
</div>
</div>
</a>
</div>

View File

@ -2,24 +2,56 @@
import { browser } from "$app/environment"; import { browser } from "$app/environment";
import { page } from "$app/state"; import { page } from "$app/state";
import { duration, fade } from "$lib/animation"; import { duration, fade } from "$lib/animation";
import { effects, setTheme } from "$lib/store/index.svelte"; import { effects, files, goingLeft, setTheme } from "$lib/store/index.svelte";
import clsx from "clsx"; import clsx from "clsx";
import { MoonIcon, SunIcon } from "lucide-svelte"; import {
InfoIcon,
MoonIcon,
RefreshCw,
SettingsIcon,
SunIcon,
UploadIcon,
} from "lucide-svelte";
import { quintOut } from "svelte/easing"; import { quintOut } from "svelte/easing";
import Panel from "../visual/Panel.svelte"; import Panel from "../../visual/Panel.svelte";
import Logo from "../visual/svg/Logo.svelte"; import Logo from "../../visual/svg/Logo.svelte";
import { beforeNavigate } from "$app/navigation";
type Props = { const items = $derived<
items: { {
name: string; name: string;
url: string; url: string;
activeMatch: (pathname: string) => boolean; activeMatch: (pathname: string) => boolean;
icon: any; icon: any;
badge?: number; badge?: number;
}[]; }[]
}; >([
{
let { items }: Props = $props(); name: "Upload",
url: "/",
activeMatch: (pathname) => pathname === "/",
icon: UploadIcon,
},
{
name: "Convert",
url: "/convert",
activeMatch: (pathname) => pathname === "/convert",
icon: RefreshCw,
badge: files.files.length,
},
{
name: "Settings",
url: "/settings",
activeMatch: (pathname) => pathname.startsWith("/settings"),
icon: SettingsIcon,
},
{
name: "About",
url: "/about",
activeMatch: (pathname) => pathname.startsWith("/about"),
icon: InfoIcon,
},
]);
let links = $state<HTMLAnchorElement[]>([]); let links = $state<HTMLAnchorElement[]>([]);
let container = $state<HTMLDivElement>(); let container = $state<HTMLDivElement>();
@ -33,6 +65,20 @@
const selectedIndex = $derived( const selectedIndex = $derived(
items.findIndex((i) => i.activeMatch(page.url.pathname)), items.findIndex((i) => i.activeMatch(page.url.pathname)),
); );
beforeNavigate((e) => {
const oldIndex = items.findIndex((i) =>
i.activeMatch(e.from?.url.pathname || ""),
);
const newIndex = items.findIndex((i) =>
i.activeMatch(e.to?.url.pathname || ""),
);
if (newIndex < oldIndex) {
goingLeft.set(true)
} else {
goingLeft.set(false)
}
});
</script> </script>
{#snippet link(item: (typeof items)[0], index: number)} {#snippet link(item: (typeof items)[0], index: number)}

View File

@ -0,0 +1,7 @@
<script lang="ts">
import Navbar from "./Base.svelte";
</script>
<div class="hidden md:flex p-8 w-screen justify-center">
<Navbar />
</div>

View File

@ -0,0 +1,9 @@
<script lang="ts">
import Navbar from "./Base.svelte";
</script>
<div class="fixed md:hidden bottom-0 left-0 w-screen p-8 justify-center z-50">
<div class="flex flex-col justify-center items-center">
<Navbar />
</div>
</div>

View File

@ -0,0 +1,2 @@
export { default as Desktop } from "./Desktop.svelte";
export { default as Mobile } from "./Mobile.svelte";

View File

@ -0,0 +1,44 @@
<script lang="ts">
import { page } from "$app/state";
import { duration } from "$lib/animation";
import { goingLeft, isMobile } from "$lib/store/index.svelte";
import { quintOut } from "svelte/easing";
import { fly, fade } from "$lib/animation";
let { children } = $props();
</script>
<div class="grid grid-rows-1 grid-cols-1 h-full flex-grow">
{#key page.url.pathname}
<div
class="row-start-1 col-start-1"
in:fly={{
x: $goingLeft ? -window.innerWidth : window.innerWidth,
duration,
easing: quintOut,
delay: 25,
}}
out:fly={{
x: $goingLeft ? window.innerWidth : -window.innerWidth,
duration,
easing: quintOut,
}}
>
<div
class="flex flex-col h-full pb-32"
in:fade={{
duration,
easing: quintOut,
delay: $isMobile ? 0 : 100,
}}
out:fade={{
duration,
easing: quintOut,
delay: $isMobile ? 0 : 200,
}}
>
{@render children()}
</div>
</div>
{/key}
</div>

View File

@ -0,0 +1,46 @@
<script lang="ts">
import { duration, fade } from "$lib/animation";
import { dropping, effects } from "$lib/store/index.svelte";
import { quintOut } from "svelte/easing";
</script>
{#if $dropping}
<div
class="fixed w-screen h-screen opacity-40 dynadark:opacity-20 z-[100] pointer-events-none blur-2xl {$effects
? 'dragoverlay'
: 'bg-accent-blue'}"
class:_dragover={dropping && $effects}
transition:fade={{
duration,
easing: quintOut,
}}
></div>
{/if}
<style>
.dragoverlay {
animation: dragoverlay-animation 3s infinite linear;
}
@keyframes dragoverlay-animation {
0% {
@apply bg-accent-pink;
}
25% {
@apply bg-accent-blue;
}
50% {
@apply bg-accent-purple;
}
75% {
@apply bg-accent-red;
}
100% {
@apply bg-accent-pink;
}
}
</style>

View File

@ -0,0 +1,5 @@
export { default as UploadRegion } from './UploadRegion.svelte';
export { default as Gradients } from './Gradients.svelte';
export { default as PageContent } from './PageContent.svelte';
export { default as MobileLogo } from './MobileLogo.svelte';
export { default as Footer } from './Footer.svelte';

View File

@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import FancyInput from "$lib/components/functional/FancyInput.svelte"; import FancyInput from "$lib/components/functional/FancyInput.svelte";
import Panel from "$lib/components/visual/Panel.svelte"; import Panel from "$lib/components/visual/Panel.svelte";
import { effects } from "$lib/store/index.svelte";
import { import {
CalendarHeartIcon, CalendarHeartIcon,
HandCoinsIcon, HandCoinsIcon,
@ -41,29 +42,29 @@
<div class="flex flex-col gap-3 w-full"> <div class="flex flex-col gap-3 w-full">
<div class="flex gap-3 w-full"> <div class="flex gap-3 w-full">
<button <button
class="btn flex-1 p-4 rounded-lg bg-accent-red text-black flex items-center justify-center" class="btn {$effects ? "" : "!scale-100"} flex-1 p-4 rounded-lg bg-accent-red text-black flex items-center justify-center"
> >
<HandCoinsIcon size="24" class="inline-block mr-2" /> <HandCoinsIcon size="24" class="inline-block mr-2" />
One-time One-time
</button> </button>
<button <button
class="btn flex-1 p-4 rounded-lg bg-button text-black dynadark:text-white flex items-center justify-center" class="btn {$effects ? "" : "!scale-100"} flex-1 p-4 rounded-lg bg-button text-black dynadark:text-white flex items-center justify-center"
> >
<CalendarHeartIcon size="24" class="inline-block mr-2" /> <CalendarHeartIcon size="24" class="inline-block mr-2" />
Monthly Monthly
</button> </button>
</div> </div>
<div class="flex gap-3 w-full"> <div class="flex gap-3 w-full">
<button class="btn bg-accent-red text-black p-4 rounded-lg flex-1" <button class="btn {$effects ? "" : "!scale-100"} bg-accent-red text-black p-4 rounded-lg flex-1"
>$1 USD</button >$1 USD</button
> >
<button <button
class="btn bg-button text-black dynadark:text-white p-4 rounded-lg flex-1" class="btn {$effects ? "" : "!scale-100"} bg-button text-black dynadark:text-white p-4 rounded-lg flex-1"
>$5 USD</button >$5 USD</button
> >
<button <button
class="btn bg-button text-black dynadark:text-white p-4 rounded-lg flex-1" class="btn {$effects ? "" : "!scale-100"} bg-button text-black dynadark:text-white p-4 rounded-lg flex-1"
>$10 USD</button >$10 USD</button
> >
<!-- <div class="relative flex items-center flex-[2]"> <!-- <div class="relative flex items-center flex-[2]">
@ -86,7 +87,7 @@
</p> </p>
<button <button
class="btn flex-1 p-3 rounded-3xl bg-accent-red text-black flex items-center justify-center" class="btn {$effects ? "" : "!scale-100"} flex-1 p-3 rounded-3xl bg-accent-red text-black flex items-center justify-center"
> >
<WalletIcon size="24" class="inline-block mr-2" /> <WalletIcon size="24" class="inline-block mr-2" />
Pay now Pay now

View File

@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import Panel from "$lib/components/visual/Panel.svelte"; import Panel from "$lib/components/visual/Panel.svelte";
import { DISCORD_URL, GITHUB_URL_VERT } from "$lib/consts"; import { DISCORD_URL, GITHUB_URL_VERT } from "$lib/consts";
import { effects } from "$lib/store/index.svelte";
import { GithubIcon, LinkIcon, MessageCircleMoreIcon } from "lucide-svelte"; import { GithubIcon, LinkIcon, MessageCircleMoreIcon } from "lucide-svelte";
</script> </script>
@ -18,7 +19,7 @@
href={DISCORD_URL} href={DISCORD_URL}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="btn flex-1 gap-2 p-4 rounded-full bg-button text-black dynadark:text-white flex items-center justify-center" class="btn {$effects ? "" : "!scale-100"} flex-1 gap-2 p-4 rounded-full bg-button text-black dynadark:text-white flex items-center justify-center"
> >
<MessageCircleMoreIcon size="24" class="inline-block mr-2" /> <MessageCircleMoreIcon size="24" class="inline-block mr-2" />
Discord Discord
@ -27,7 +28,7 @@
href={GITHUB_URL_VERT} href={GITHUB_URL_VERT}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="btn flex-1 gap-2 p-4 rounded-full bg-button text-black dynadark:text-white flex items-center justify-center" class="btn {$effects ? "" : "!scale-100"} flex-1 gap-2 p-4 rounded-full bg-button text-black dynadark:text-white flex items-center justify-center"
> >
<GithubIcon size="24" class="inline-block mr-2" /> <GithubIcon size="24" class="inline-block mr-2" />
Source Source

View File

@ -55,6 +55,11 @@
if (effectsUnsubscribe) effectsUnsubscribe(); if (effectsUnsubscribe) effectsUnsubscribe();
if (themeUnsubscribe) themeUnsubscribe(); if (themeUnsubscribe) themeUnsubscribe();
}); });
$effect(() => {
updateEffectsClasses($effects);
updateThemeClasses($theme);
});
</script> </script>
<Panel class="flex flex-col gap-8 p-6"> <Panel class="flex flex-col gap-8 p-6">
@ -80,7 +85,7 @@
<button <button
bind:this={lightElement} bind:this={lightElement}
onclick={() => setTheme("light")} onclick={() => setTheme("light")}
class="btn flex-1 p-4 rounded-lg text-black dynadark:text-white flex items-center justify-center" class="btn {$effects ? "" : "!scale-100"} flex-1 p-4 rounded-lg text-black dynadark:text-white flex items-center justify-center"
> >
<SunIcon size="24" class="inline-block mr-2" /> <SunIcon size="24" class="inline-block mr-2" />
Light Light
@ -89,7 +94,7 @@
<button <button
bind:this={darkElement} bind:this={darkElement}
onclick={() => setTheme("dark")} onclick={() => setTheme("dark")}
class="btn flex-1 p-4 rounded-lg text-black flex items-center justify-center" class="btn {$effects ? "" : "!scale-100"} flex-1 p-4 rounded-lg text-black flex items-center justify-center"
> >
<MoonIcon size="24" class="inline-block mr-2" /> <MoonIcon size="24" class="inline-block mr-2" />
Dark Dark
@ -110,7 +115,7 @@
<button <button
bind:this={enableEffectsElement} bind:this={enableEffectsElement}
onclick={() => setEffects(true)} onclick={() => setEffects(true)}
class="btn flex-1 p-4 rounded-lg text-black dynadark:text-white flex items-center justify-center" class="btn {$effects ? "" : "!scale-100"} flex-1 p-4 rounded-lg text-black dynadark:text-white flex items-center justify-center"
> >
<PlayIcon size="24" class="inline-block mr-2" /> <PlayIcon size="24" class="inline-block mr-2" />
Enable Enable
@ -119,7 +124,7 @@
<button <button
bind:this={disableEffectsElement} bind:this={disableEffectsElement}
onclick={() => setEffects(false)} onclick={() => setEffects(false)}
class="btn flex-1 p-4 rounded-lg text-black dynadark:text-white flex items-center justify-center" class="btn {$effects ? "" : "!scale-100"} flex-1 p-4 rounded-lg text-black dynadark:text-white flex items-center justify-center"
> >
<PauseIcon size="24" class="inline-block mr-2" /> <PauseIcon size="24" class="inline-block mr-2" />
Disable Disable

View File

@ -5,6 +5,7 @@
import type { ISettings } from "./index.svelte"; import type { ISettings } from "./index.svelte";
import clsx from "clsx"; import clsx from "clsx";
import Dropdown from "$lib/components/functional/Dropdown.svelte"; import Dropdown from "$lib/components/functional/Dropdown.svelte";
import { vertdLoaded } from "$lib/store/index.svelte";
let vertdCommit = $state<string | null>(null); let vertdCommit = $state<string | null>(null);
let abortController: AbortController | null = null; let abortController: AbortController | null = null;
@ -21,17 +22,23 @@
fetch(`${settings.vertdURL}/api/version`, { signal }) fetch(`${settings.vertdURL}/api/version`, { signal })
.then((res) => { .then((res) => {
if (!res.ok) throw new Error("bad response"); if (!res.ok) throw new Error("bad response");
vertdLoaded.set(false);
return res.json(); return res.json();
}) })
.then((data) => { .then((data) => {
vertdCommit = data.data; vertdCommit = data.data;
vertdLoaded.set(true);
}) })
.catch((err) => { .catch((err) => {
if (err.name !== "AbortError") vertdCommit = null; if (err.name !== "AbortError") {
vertdCommit = null;
vertdLoaded.set(false);
}
}); });
} else { } else {
if (abortController) abortController.abort(); if (abortController) abortController.abort();
vertdCommit = null; vertdCommit = null;
vertdLoaded.set(false);
} }
return () => { return () => {
@ -48,7 +55,7 @@
class="inline-block -mt-1 mr-2 bg-accent-red p-2 rounded-full overflow-visible" class="inline-block -mt-1 mr-2 bg-accent-red p-2 rounded-full overflow-visible"
color="black" color="black"
/> />
Converting Video Video conversion
</h2> </h2>
<p <p
class={clsx("text-sm font-normal", { class={clsx("text-sm font-normal", {

View File

@ -32,16 +32,6 @@ function addToast(
exit: 500, exit: 500,
}; };
// if "disappearing" not set, default error/warning to infinite duration
if (disappearing === undefined) {
switch (type) {
case "error":
case "warning":
durations.stay = 86400000; // 24h cause why not
break;
}
}
const newToast: Toast = { const newToast: Toast = {
id, id,
type, type,

View File

@ -229,6 +229,9 @@ export function setEffects(effectsEnabled: boolean) {
export const files = new Files(); export const files = new Files();
export const showGradient = writable(true); export const showGradient = writable(true);
export const gradientColor = writable(""); export const gradientColor = writable("");
export const goingLeft = writable(false);
export const dropping = writable(false);
export const vertdLoaded = writable(false);
export const isMobile = writable(false); export const isMobile = writable(false);
export const effects = writable(true); export const effects = writable(true);

View File

@ -1,85 +1,35 @@
<script lang="ts"> <script lang="ts">
import { page } from "$app/state"; import { onMount } from "svelte";
import { beforeNavigate, goto } from "$app/navigation"; import { goto } from "$app/navigation";
import { PUB_HOSTNAME, PUB_PLAUSIBLE_URL } from "$env/static/public";
import { duration, fly } from "$lib/animation";
import VertVBig from "$lib/assets/vert-bg.svg?component";
import featuredImage from "$lib/assets/VERT_Feature.webp";
import Navbar from "$lib/components/functional/Navbar.svelte";
import Footer from "$lib/components/visual/Footer.svelte";
import Logo from "$lib/components/visual/svg/Logo.svelte";
import { fade } from "$lib/animation"; import { PUB_HOSTNAME, PUB_PLAUSIBLE_URL } from "$env/static/public";
import { VERT_NAME } from "$lib/consts";
import Toast from "$lib/components/visual/Toast.svelte";
import * as Layout from "$lib/components/layout";
import * as Navbar from "$lib/components/layout/Navbar";
import featuredImage from "$lib/assets/VERT_Feature.webp";
import { type Toast as ToastType, toasts } from "$lib/store/ToastProvider";
import { Settings } from "$lib/sections/settings/index.svelte";
import { import {
files, files,
gradientColor,
isMobile, isMobile,
effects, effects,
showGradient,
theme, theme,
dropping,
} from "$lib/store/index.svelte"; } from "$lib/store/index.svelte";
import {
InfoIcon,
RefreshCw,
SettingsIcon,
UploadIcon,
} from "lucide-svelte";
import { onMount } from "svelte";
import { quintOut } from "svelte/easing";
import "../app.scss"; import "../app.scss";
import { DISCORD_URL, GITHUB_URL_VERT, VERT_NAME } from "$lib/consts";
import { type Toast as ToastType, toasts } from "$lib/store/ToastProvider";
import Toast from "$lib/components/visual/Toast.svelte";
import { Settings } from "$lib/sections/settings/index.svelte";
let { children } = $props(); let { children } = $props();
let dropping = $state(false);
let goingLeft = $state(false);
let toastList = $state<ToastType[]>([]); let toastList = $state<ToastType[]>([]);
toasts.subscribe((value) => { toasts.subscribe((value) => {
toastList = value as ToastType[]; toastList = value as ToastType[];
}); });
const items = $derived<
{
name: string;
url: string;
activeMatch: (pathname: string) => boolean;
icon: any;
badge?: number;
}[]
>([
{
name: "Upload",
url: "/",
activeMatch: (pathname) => pathname === "/",
icon: UploadIcon,
},
{
name: "Convert",
url: "/convert",
activeMatch: (pathname) => pathname === "/convert",
icon: RefreshCw,
badge: files.files.length,
},
{
name: "Settings",
url: "/settings",
activeMatch: (pathname) => pathname.startsWith("/settings"),
icon: SettingsIcon,
},
{
name: "About",
url: "/about",
activeMatch: (pathname) => pathname.startsWith("/about"),
icon: InfoIcon,
},
]);
const dropFiles = (e: DragEvent) => { const dropFiles = (e: DragEvent) => {
e.preventDefault(); e.preventDefault();
dropping = false; dropping.set(false);
const oldLength = files.files.length; const oldLength = files.files.length;
files.add(e.dataTransfer?.files); files.add(e.dataTransfer?.files);
if (oldLength !== files.files.length) goto("/convert"); if (oldLength !== files.files.length) goto("/convert");
@ -87,7 +37,7 @@
const handleDrag = (e: DragEvent, drag: boolean) => { const handleDrag = (e: DragEvent, drag: boolean) => {
e.preventDefault(); e.preventDefault();
dropping = drag; dropping.set(drag);
}; };
onMount(() => { onMount(() => {
@ -103,20 +53,6 @@
Settings.instance.load(); Settings.instance.load();
}); });
beforeNavigate((e) => {
const oldIndex = items.findIndex((i) =>
i.activeMatch(e.from?.url.pathname || ""),
);
const newIndex = items.findIndex((i) =>
i.activeMatch(e.to?.url.pathname || ""),
);
if (newIndex < oldIndex) {
goingLeft = true;
} else {
goingLeft = false;
}
});
</script> </script>
<svelte:head> <svelte:head>
@ -158,6 +94,7 @@
<script src="/coi-serviceworker.min.js"></script> <script src="/coi-serviceworker.min.js"></script>
</svelte:head> </svelte:head>
<!-- FIXME: if user resizes between desktop/mobile, highlight of page disappears (only shows on original size) -->
<div <div
class="flex flex-col min-h-screen h-full" class="flex flex-col min-h-screen h-full"
ondrop={dropFiles} ondrop={dropFiles}
@ -166,77 +103,18 @@
ondragleave={(e) => handleDrag(e, false)} ondragleave={(e) => handleDrag(e, false)}
role="region" role="region"
> >
{#if dropping} <Layout.UploadRegion />
<div
class="fixed w-screen h-screen opacity-40 dynadark:opacity-20 z-[100] pointer-events-none blur-2xl {$effects
? 'dragoverlay'
: 'bg-accent-blue'}"
class:_dragover={dropping && $effects}
transition:fade={{
duration,
easing: quintOut,
}}
></div>
{/if}
<!-- FIXME: if user resizes between desktop/mobile, highlight of page disappears (only shows on original size) -->
<div> <div>
<!-- Mobile logo --> <Layout.MobileLogo />
<div class="flex md:hidden justify-center items-center pb-8 pt-4"> <Navbar.Desktop />
<a
class="flex items-center justify-center bg-panel p-2 rounded-[20px] shadow-panel"
href="/"
>
<div
class="h-14 bg-accent rounded-[14px] flex items-center justify-center"
>
<div class="w-28 h-5">
<Logo />
</div>
</div>
</a>
</div> </div>
<!-- Desktop navbar --> <!--
<div class="hidden md:flex p-8 w-screen justify-center"> SvelteKit throws the following warning when developing - safe to ignore as we render the children in this component:
<Navbar {items} /> `<slot />` or `{@render ...}` tag missing — inner content will not be rendered
</div> -->
</div> <Layout.PageContent {children} />
<div class="grid grid-rows-1 grid-cols-1 h-full flex-grow">
{#key page.url.pathname}
<div
class="row-start-1 col-start-1"
in:fly={{
x: goingLeft ? -window.innerWidth : window.innerWidth,
duration,
easing: quintOut,
delay: 25,
}}
out:fly={{
x: goingLeft ? window.innerWidth : -window.innerWidth,
duration,
easing: quintOut,
}}
>
<div
class="flex flex-col h-full pb-32"
in:fade={{
duration,
easing: quintOut,
delay: $isMobile ? 0 : 100,
}}
out:fade={{
duration,
easing: quintOut,
delay: $isMobile ? 0 : 200,
}}
>
{@render children()}
</div>
</div>
{/key}
</div>
<div class="fixed bottom-28 md:bottom-0 right-0 p-4 space-y-4 z-50"> <div class="fixed bottom-28 md:bottom-0 right-0 p-4 space-y-4 z-50">
{#each toastList as { id, type, message, durations }} {#each toastList as { id, type, message, durations }}
@ -245,118 +123,10 @@
</div> </div>
<div> <div>
<div <Layout.Footer />
class="hidden md:block w-full h-14 border-t border-separator fixed bottom-0 mt-12" <Navbar.Mobile />
>
<Footer
class="w-full h-full"
items={{
//"Privacy policy": "#",
"Source code": GITHUB_URL_VERT,
"Discord server": DISCORD_URL,
}}
/>
</div>
<!-- Mobile navbar -->
<div
class="fixed md:hidden bottom-0 left-0 w-screen p-8 justify-center z-50"
>
<div class="flex flex-col justify-center items-center">
<Navbar {items} />
</div>
</div>
</div> </div>
</div> </div>
<!-- Gradients placed here to prevent it overlapping in transitions --> <!-- Gradients placed here to prevent it overlapping in transitions -->
{#if page.url.pathname === "/"} <Layout.Gradients />
<div
class="fixed -z-30 top-0 left-0 w-screen h-screen flex items-center justify-center overflow-hidden"
>
<VertVBig
class="fill-[--fg] opacity-10 dynadark:opacity-5 scale-[200%] md:scale-[80%]"
/>
</div>
<div
id="gradient-bg"
class="fixed top-0 left-0 w-screen h-screen -z-40 pointer-events-none"
style="background: var(--bg-gradient);"
></div>
{:else if page.url.pathname === "/convert" && $showGradient}
<div
id="gradient-bg"
class="fixed top-0 left-0 w-screen h-screen -z-40 pointer-events-none"
style="background: var(--bg-gradient-{$gradientColor || 'pink'});"
></div>
{:else if page.url.pathname === "/convert" && files.files.length === 1 && files.files[0].blobUrl}
<div
class="fixed w-screen h-screen opacity-75 overflow-hidden top-0 left-0 -z-50 pointer-events-none grid grid-cols-1 grid-rows-1 scale-105"
>
<div
class="w-full relative"
transition:fade={{
duration,
easing: quintOut,
}}
>
<img
class="object-cover w-full h-full blur-md"
src={files.files[0].blobUrl}
alt={files.files[0].name}
/>
<div
class="absolute top-0 left-0 w-full h-full"
style="background: var(--bg-gradient-image);"
></div>
<!-- <div class="absolute bottom-0 left-0 w-full h-full">
<ProgressiveBlur
direction="bottom"
endIntensity={256}
iterations={8}
fadeTo="var(--bg)"
/>
</div> -->
</div>
</div>
{:else if page.url.pathname === "/settings"}
<div
id="gradient-bg"
class="fixed top-0 left-0 w-screen h-screen -z-40 pointer-events-none"
style="background: var(--bg-gradient-blue);"
></div>
{:else if page.url.pathname === "/about"}
<div
id="gradient-bg"
class="fixed top-0 left-0 w-screen h-screen -z-40 pointer-events-none"
style="background: var(--bg-gradient-pink);"
></div>
{/if}
<style>
.dragoverlay {
animation: dragoverlay-animation 3s infinite linear;
}
@keyframes dragoverlay-animation {
0% {
@apply bg-accent-pink;
}
25% {
@apply bg-accent-blue;
}
50% {
@apply bg-accent-purple;
}
75% {
@apply bg-accent-red;
}
100% {
@apply bg-accent-pink;
}
}
</style>

View File

@ -10,7 +10,18 @@
// -- JovannMC // -- JovannMC
import Uploader from "$lib/components/functional/Uploader.svelte"; import Uploader from "$lib/components/functional/Uploader.svelte";
import { converters } from "$lib/converters";
import { AudioLines, Check, Film, Image } from "lucide-svelte"; import { AudioLines, Check, Film, Image } from "lucide-svelte";
const getSupportedFormats = (name: string) =>
converters.find((c) => c.name === name)?.supportedFormats.join(", ") ||
"none";
const supportedFormats = {
images: getSupportedFormats("libvips"),
audio: getSupportedFormats("ffmpeg"),
video: getSupportedFormats("vertd"),
};
</script> </script>
<div class="max-w-6xl w-full mx-auto px-6 md:px-8"> <div class="max-w-6xl w-full mx-auto px-6 md:px-8">
@ -40,7 +51,7 @@
<hr /> <hr />
<div class="mt-10 md:mt-16"> <div class="mt-10 md:mt-16">
<h2 class="text-center text-4xl">VERT Supports...</h2> <h2 class="text-center text-4xl">VERT supports...</h2>
<div class="grid gap-4 md:grid-cols-3 mt-8"> <div class="grid gap-4 md:grid-cols-3 mt-8">
<div class="file-category-card"> <div class="file-category-card">
@ -51,7 +62,13 @@
<span>Images</span> <span>Images</span>
</div> </div>
<div class="flex flex-col text-center justify-center">
<p>Animated images are not supported (yet).</p> <p>Animated images are not supported (yet).</p>
<p>
<b>Supported formats:</b>
{supportedFormats.images}
</p>
</div>
</div> </div>
<div class="file-category-card"> <div class="file-category-card">
@ -62,9 +79,15 @@
<span>Audio</span> <span>Audio</span>
</div> </div>
<div class="flex flex-col text-center justify-between">
<p class="flex items-center justify-center gap-2"> <p class="flex items-center justify-center gap-2">
<Check size="20" /> Fully supported <Check size="20" /> Fully supported
</p> </p>
<p>
<b>Supported formats:</b>
{supportedFormats.audio}
</p>
</div>
</div> </div>
<div class="file-category-card"> <div class="file-category-card">
@ -74,13 +97,16 @@
</div> </div>
<span>Video *</span> <span>Video *</span>
</div> </div>
<div class="flex flex-col text-center justify-between">
<p> <p>
Video requires special setup. <a Video requires special setup. <a
target="_blank" target="_blank"
href="https://github.com/VERT-sh/VERT/wiki/How-to-convert-video-with-VERT" href="https://github.com/VERT-sh/VERT/wiki/How-to-convert-video-with-VERT"
>Learn more</a >Learn more</a
> >.
</p> </p>
<p><b>Supported formats:</b> {supportedFormats.video}</p>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -4,10 +4,13 @@
import Uploader from "$lib/components/functional/Uploader.svelte"; import Uploader from "$lib/components/functional/Uploader.svelte";
import Panel from "$lib/components/visual/Panel.svelte"; import Panel from "$lib/components/visual/Panel.svelte";
import ProgressBar from "$lib/components/visual/ProgressBar.svelte"; import ProgressBar from "$lib/components/visual/ProgressBar.svelte";
import { converters } from "$lib/converters";
import { import {
effects,
files, files,
gradientColor, gradientColor,
showGradient, showGradient,
vertdLoaded,
} from "$lib/store/index.svelte"; } from "$lib/store/index.svelte";
import { VertFile } from "$lib/types"; import { VertFile } from "$lib/types";
import { import {
@ -24,12 +27,6 @@
} from "lucide-svelte"; } from "lucide-svelte";
$effect(() => { $effect(() => {
if (files.files.length === 1 && files.files[0].blobUrl) {
showGradient.set(false);
} else {
showGradient.set(true);
}
// Set gradient color depending on the file types // Set gradient color depending on the file types
// TODO: if more file types added, add a "fileType" property to the file object // TODO: if more file types added, add a "fileType" property to the file object
const allAudio = files.files.every( const allAudio = files.files.every(
@ -44,6 +41,12 @@
(file) => file.converter?.name === "vertd", (file) => file.converter?.name === "vertd",
); );
if (files.files.length === 1 && files.files[0].blobUrl && !allVideos) {
showGradient.set(false);
} else {
showGradient.set(true);
}
if ( if (
files.files.length === 0 || files.files.length === 0 ||
(!allAudio && !allImages && !allVideos) (!allAudio && !allImages && !allVideos)
@ -95,12 +98,38 @@
</button> </button>
</div> </div>
{#if !file.converter} {#if !file.converter}
{#if file.name.startsWith("vertd")}
<div
class="h-full flex flex-col text-center justify-center text-failure"
>
<p class="font-body font-bold">
We can't convert this file.
</p>
<p class="font-normal">
what are you doing..? you're supposed to run the vertd
server!
</p>
</div>
{:else}
<div
class="h-full flex flex-col text-center justify-center text-failure"
>
<p class="font-body font-bold">
We can't convert this file.
</p>
<p class="font-normal">
Only image, video, and audio files are supported
</p>
</div>
{/if}
{:else if isVideo && !$vertdLoaded}
<div <div
class="h-full flex flex-col text-center justify-center text-failure" class="h-full flex flex-col text-center justify-center text-failure"
> >
<p class="font-body font-bold">We can't convert this file.</p> <p class="font-body font-bold">We can't convert this file.</p>
<p class="font-normal"> <p class="font-normal">
Only image, video, and audio files are supported Could not find the vertd instance to start video conversion.
Are you sure the instance URL is set correctly?
</p> </p>
</div> </div>
{:else} {:else}
@ -148,7 +177,9 @@
/> />
<div class="w-full flex items-center justify-between"> <div class="w-full flex items-center justify-between">
<button <button
class="btn p-0 w-14 h-14 text-black {isAudio class="btn {$effects
? ''
: '!scale-100'} p-0 w-14 h-14 text-black {isAudio
? 'bg-accent-purple' ? 'bg-accent-purple'
: isVideo : isVideo
? 'bg-accent-red' ? 'bg-accent-red'
@ -159,7 +190,9 @@
<RotateCwIcon size="24" /> <RotateCwIcon size="24" />
</button> </button>
<button <button
class="btn p-0 w-14 h-14" class="btn {$effects
? ''
: '!scale-100'} p-0 w-14 h-14"
onclick={file.download} onclick={file.download}
disabled={!file.result} disabled={!file.result}
> >