chore: rework launcher design
This commit is contained in:
1 parent
f9b38ec9eb
commit
1f845f1282
10 files changed
+709
-677
No files matched your search
@@ -15,7 +15,7 @@
|
|||||||
{
|
{
|
||||||
"title": "EZPPLauncher",
|
"title": "EZPPLauncher",
|
||||||
"width": 1000,
|
"width": 1000,
|
||||||
"height": 737,
|
"height": 650,
|
||||||
"decorations": false,
|
"decorations": false,
|
||||||
"resizable": false,
|
"resizable": false,
|
||||||
"maximizable": false
|
"maximizable": false
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
//TODO: maybe dynamic background images fetched from ezpp?
|
||||||
|
|
||||||
|
const COUNT = 120;
|
||||||
|
|
||||||
|
function rand(min: number, max: number) {
|
||||||
|
return Math.random() * (max - min) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
const streaks = Array.from({ length: COUNT }).map(() => {
|
||||||
|
const depth = rand(0, 2);
|
||||||
|
|
||||||
|
const scale = 0.6 + depth * 0.8;
|
||||||
|
const speed = 0.4 + depth * 1.35;
|
||||||
|
|
||||||
|
const distance = 150;
|
||||||
|
const duration = distance / speed;
|
||||||
|
|
||||||
|
return {
|
||||||
|
width: rand(180, 320) * scale,
|
||||||
|
height: rand(16, 32) * scale,
|
||||||
|
x: rand(-80, 100),
|
||||||
|
y: rand(-80, 100),
|
||||||
|
opacity: 0.25 + depth * 0.65,
|
||||||
|
duration,
|
||||||
|
delay: rand(0, duration),
|
||||||
|
scale,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="absolute bg blur-[2px]">
|
||||||
|
<div
|
||||||
|
class="relative h-full w-full from-0% to-90% bg-gradient-to-b from-transparent to-black z-50"
|
||||||
|
></div>
|
||||||
|
{#each streaks as s}
|
||||||
|
<div
|
||||||
|
class="streak drop-shadow-lg"
|
||||||
|
style="
|
||||||
|
width:{s.width}px;
|
||||||
|
height:{s.height}px;
|
||||||
|
left:{s.x}%;
|
||||||
|
top:{s.y}%;
|
||||||
|
opacity:{s.opacity};
|
||||||
|
animation-duration:{s.duration}s;
|
||||||
|
animation-delay:-{s.delay}s;
|
||||||
|
"
|
||||||
|
></div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.bg {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
background: linear-gradient(180deg, #8f2f64 0%, #963f75 40%, #9a6c7f 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.streak {
|
||||||
|
position: absolute;
|
||||||
|
background: #d6f3ff;
|
||||||
|
border-radius: 999px;
|
||||||
|
transform: rotate(-35deg);
|
||||||
|
animation: move linear infinite;
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes move {
|
||||||
|
from {
|
||||||
|
transform: translate(-150vw, 150vh) rotate(-35deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translate(150vw, -150vh) rotate(-35deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Ellipsis from 'lucide-svelte/icons/ellipsis';
|
||||||
|
import Play from 'lucide-svelte/icons/play';
|
||||||
|
|
||||||
|
const prop: {
|
||||||
|
downloading: boolean;
|
||||||
|
progress: number;
|
||||||
|
text: string;
|
||||||
|
subtext?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
onClick?: () => void;
|
||||||
|
} = $props();
|
||||||
|
|
||||||
|
const radius = 16;
|
||||||
|
const circumference = 2 * Math.PI * radius;
|
||||||
|
const strokeDashoffset = $derived(circumference - (prop.progress / 100) * circumference);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="group flex items-center h-12 rounded-full bg-pink-400 disabled:bg-pink-400/70 shadow-lg shadow-primary/25 transition-all not-disabled:hover:shadow-primary/40 not-disabled:hover:brightness-110 not-disabled:active:scale-[0.98] cursor-pointer w-fit"
|
||||||
|
disabled={prop.disabled}
|
||||||
|
onclick={prop.onClick}
|
||||||
|
>
|
||||||
|
<div class="relative size-12 shrink-0 flex items-center justify-center">
|
||||||
|
{#if prop.downloading}
|
||||||
|
{#if prop.progress === -1}
|
||||||
|
<svg class="absolute inset-0 animate-spin" viewBox="0 0 48 48">
|
||||||
|
<circle class="stroke-black/10" cx="24" cy="24" r={radius} fill="none" stroke-width="3" />
|
||||||
|
<circle
|
||||||
|
class="stroke-pink-950"
|
||||||
|
cx="24"
|
||||||
|
cy="24"
|
||||||
|
r={radius}
|
||||||
|
fill="none"
|
||||||
|
stroke-width="3"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-dasharray={circumference}
|
||||||
|
stroke-dashoffset={circumference * 0.7}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{:else}
|
||||||
|
<svg class="absolute inset-0 -rotate-90" viewBox="0 0 48 48">
|
||||||
|
<circle
|
||||||
|
cx="24"
|
||||||
|
cy="24"
|
||||||
|
r={radius}
|
||||||
|
fill="none"
|
||||||
|
stroke="rgba(0,0,0,0.2)"
|
||||||
|
stroke-width="3"
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
cx="24"
|
||||||
|
cy="24"
|
||||||
|
r={radius}
|
||||||
|
fill="none"
|
||||||
|
stroke="rgba(0,0,0,0.7)"
|
||||||
|
stroke-width="3"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-dasharray={circumference}
|
||||||
|
stroke-dashoffset={strokeDashoffset}
|
||||||
|
class="transition-[stroke-dashoffset] duration-100"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
<div class="size-9 rounded-full bg-black/30 flex items-center justify-center relative z-10">
|
||||||
|
{#if prop.downloading}
|
||||||
|
{#if prop.progress === -1}
|
||||||
|
<Ellipsis class="size-4 text-primary-foreground fill-current animate-pulse" />
|
||||||
|
{:else}
|
||||||
|
<span class="text-[10px] font-bold text-primary-foreground">
|
||||||
|
{prop.progress.toFixed()}
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
{:else}
|
||||||
|
<Play class="size-4 text-primary-foreground fill-current ml-0.5" />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pr-6 pl-1 text-center w-52">
|
||||||
|
<p class="text-sm font-bold text-primary-foreground leading-tight">
|
||||||
|
{prop.text}
|
||||||
|
</p>
|
||||||
|
{#if prop.subtext}
|
||||||
|
<p class="text-[10px] text-primary-foreground/70 leading-tight">
|
||||||
|
{prop.subtext}
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
animate(cursor, {
|
animate(cursor, {
|
||||||
duration: smoothCursor ? $cursorSmoothness : 0,
|
duration: smoothCursor ? $cursorSmoothness : 0,
|
||||||
translateX: mouseX,
|
translateX: mouseX,
|
||||||
translateY: mouseY - 50,
|
translateY: mouseY,
|
||||||
ease: (t: number) => (t - 1) ** 5 + 1,
|
ease: (t: number) => (t - 1) ** 5 + 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -19,27 +19,18 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div data-tauri-drag-region class="titlebar z-[60] border-b border-theme-800/90">
|
<div data-tauri-drag-region class="titlebar z-[100]">
|
||||||
<div
|
<div class="titlebar-button rounded transition-colors duration-75" id="titlebar-minimize">
|
||||||
class="mr-auto ms-2 flex flex-row gap-2 items-center text-[1.05rem] font-semibold pointer-events-none"
|
<Minimize size={14} />
|
||||||
>
|
|
||||||
<img src={Logo} alt="EZPP Launcher Logo" class="h-11 w-11 inline-block" />
|
|
||||||
<span>EZPPLauncher</span>
|
|
||||||
<Badge class="!text-[0.6rem] py-[0.5px] bg-primary-500 hover:bg-primary-500"
|
|
||||||
>{$launcherVersion}</Badge
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="titlebar-button rounded-lg transition-colors duration-75" id="titlebar-minimize">
|
<div class="titlebar-button close rounded transition-colors duration-75" id="titlebar-close">
|
||||||
<Minimize size={18} />
|
<Close size={14} />
|
||||||
</div>
|
|
||||||
<div class="titlebar-button close rounded-lg transition-colors duration-75" id="titlebar-close">
|
|
||||||
<Close size={18} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.titlebar {
|
.titlebar {
|
||||||
height: 50px;
|
height: 35px;
|
||||||
/* background: #040612; */
|
/* background: #040612; */
|
||||||
user-select: none;
|
user-select: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -52,14 +43,14 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
pointer-events: all !important;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
.titlebar-button {
|
.titlebar-button {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 40px;
|
width: 25px;
|
||||||
height: 40px;
|
height: 25px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,7 +150,7 @@
|
|||||||
<OsuCursor smoothCursor={$cursorSmoothening} />
|
<OsuCursor smoothCursor={$cursorSmoothening} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<Toaster richColors closeButton theme="dark" />
|
<Toaster richColors closeButton theme="dark" position="top-right" />
|
||||||
|
|
||||||
<Titlebar />
|
<Titlebar />
|
||||||
|
|
||||||
|
|||||||
+526
-526
File diff suppressed because it is too large.
Load diff
@@ -202,7 +202,7 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col items-center justify-center mt-[50px] h-[calc(100vh-50px)] w-full">
|
<div class="flex flex-col items-center justify-center h-[100vh] w-full">
|
||||||
<div class="relative w-80 h-80 flex items-center justify-center">
|
<div class="relative w-80 h-80 flex items-center justify-center">
|
||||||
<svg
|
<svg
|
||||||
class="absolute top-0 left-0 w-full h-full animate-spin"
|
class="absolute top-0 left-0 w-full h-full animate-spin"
|
||||||
|
|||||||
@@ -1,127 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import Logo from '$assets/logo.png';
|
|
||||||
import { ezppfarm } from '@/api/ezpp';
|
|
||||||
import Button from '@/components/ui/button/button.svelte';
|
|
||||||
import Input from '@/components/ui/input/input.svelte';
|
|
||||||
import Label from '@/components/ui/label/label.svelte';
|
|
||||||
import { currentView } from '@/global';
|
|
||||||
import { currentUser, userAuth } from '@/userAuthentication';
|
|
||||||
import { animate } from 'animejs';
|
|
||||||
import { LoaderCircle } from 'lucide-svelte';
|
|
||||||
import { toast } from 'svelte-sonner';
|
|
||||||
import Launch from './Launch.svelte';
|
|
||||||
import { currentUserInfo } from '@/data';
|
|
||||||
import { preferredMode, preferredType } from '@/userSettings';
|
|
||||||
|
|
||||||
let username = $state('');
|
|
||||||
let password = $state('');
|
|
||||||
let isLoading = $state(false);
|
|
||||||
|
|
||||||
let ezppLogo: HTMLImageElement | undefined = $state(undefined);
|
|
||||||
|
|
||||||
const logo_mouseenter = () => {
|
|
||||||
if (ezppLogo) {
|
|
||||||
animate(ezppLogo, {
|
|
||||||
duration: 700,
|
|
||||||
scale: 1.2,
|
|
||||||
ease: (t: number) => Math.pow(2, -5 * t) * Math.sin((t - 0.075) * 20.94) + 1 - 0.0005 * t,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const logo_mouseleave = () => {
|
|
||||||
if (ezppLogo) {
|
|
||||||
animate(ezppLogo, {
|
|
||||||
duration: 700,
|
|
||||||
scale: 1,
|
|
||||||
ease: (t: number) => (t - 1) ** 7 + 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const performLogin = async () => {
|
|
||||||
isLoading = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const loginResult = await ezppfarm.login(username, password);
|
|
||||||
if (loginResult && loginResult.user) {
|
|
||||||
toast.success('Login successful!', {
|
|
||||||
description: `Welcome back, ${loginResult.user.name}!`,
|
|
||||||
});
|
|
||||||
|
|
||||||
$userAuth.value('username').set(username);
|
|
||||||
$userAuth.value('password').set(password);
|
|
||||||
await $userAuth.save();
|
|
||||||
|
|
||||||
currentUser.set(loginResult.user);
|
|
||||||
currentView.set(Launch);
|
|
||||||
} else {
|
|
||||||
toast.error('Login failed!', {
|
|
||||||
description: 'Please check your username and password.',
|
|
||||||
});
|
|
||||||
isLoading = false;
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
toast.error('Server error occurred during login.', {
|
|
||||||
description: 'There was an issue connecting to the server. Please try again later.',
|
|
||||||
});
|
|
||||||
isLoading = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($currentUser) {
|
|
||||||
const userInfo = await ezppfarm.getUserInfo($currentUser.id);
|
|
||||||
if (userInfo) {
|
|
||||||
currentUserInfo.set(userInfo.player);
|
|
||||||
|
|
||||||
preferredMode.set(userInfo.player.info.preferred_mode);
|
|
||||||
preferredType.set(userInfo.player.info.preferred_type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="mt-[50px] h-[calc(100vh-50px)] w-full">
|
|
||||||
<div class="w-full h-full flex flex-col items-center justify-center">
|
|
||||||
<img
|
|
||||||
src={Logo}
|
|
||||||
alt="EZPPLauncher Logo"
|
|
||||||
class="w-52 h-52 mb-2"
|
|
||||||
bind:this={ezppLogo}
|
|
||||||
onmouseenter={logo_mouseenter}
|
|
||||||
onmouseleave={logo_mouseleave}
|
|
||||||
/>
|
|
||||||
<form onsubmit={performLogin} class="w-full max-w-sm">
|
|
||||||
<div class="mb-4">
|
|
||||||
<Label for="username" class="block text-sm font-medium">Username</Label>
|
|
||||||
<Input
|
|
||||||
class="mt-4 w-full bg-theme-900 border-theme-800"
|
|
||||||
type="text"
|
|
||||||
id="username"
|
|
||||||
bind:value={username}
|
|
||||||
disabled={isLoading}
|
|
||||||
autocomplete="off"
|
|
||||||
autocorrect="off"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="mb-4">
|
|
||||||
<Label for="password" class="block text-sm font-medium">Password</Label>
|
|
||||||
<Input
|
|
||||||
class="mt-4 w-full bg-theme-900 border-theme-800"
|
|
||||||
type="password"
|
|
||||||
id="password"
|
|
||||||
bind:value={password}
|
|
||||||
disabled={isLoading}
|
|
||||||
autocomplete="off"
|
|
||||||
autocorrect="off"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Button class="w-full" type="submit" disabled={isLoading}>
|
|
||||||
{#if isLoading}
|
|
||||||
<LoaderCircle class="animate-spin" />
|
|
||||||
{:else}
|
|
||||||
Login
|
|
||||||
{/if}
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -119,7 +119,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if wizardFinished}
|
{#if wizardFinished}
|
||||||
<div class="relative flex flex-col items-center justify-center mt-[50px] h-[calc(100vh-50px)]">
|
<div class="relative flex flex-col items-center justify-center h-[100vh]">
|
||||||
<div class="absolute h-fit w-fit top-1/2 left-1/2">
|
<div class="absolute h-fit w-fit top-1/2 left-1/2">
|
||||||
<Confetti amount={200} y={[-1, 1.5]} x={[-2.3, 2.3]} colorArray={['#C6A0F3']} />
|
<Confetti amount={200} y={[-1, 1.5]} x={[-2.3, 2.3]} colorArray={['#C6A0F3']} />
|
||||||
</div>
|
</div>
|
||||||
@@ -128,7 +128,7 @@
|
|||||||
<Button class="mt-4" onclick={saveConfig}>Finish</Button>
|
<Button class="mt-4" onclick={saveConfig}>Finish</Button>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="grid grid-cols-[0.41fr_1fr] mt-[50px] h-[calc(100vh-50px)]">
|
<div class="grid grid-cols-[0.41fr_1fr] h-[100vh]">
|
||||||
<div class="w-full h-full border-r border-theme-800/90 flex flex-col gap-6 p-3">
|
<div class="w-full h-full border-r border-theme-800/90 flex flex-col gap-6 p-3">
|
||||||
{#each steps as step, i (step)}
|
{#each steps as step, i (step)}
|
||||||
<div
|
<div
|
||||||
|
|||||||
Reference in new issue
Block a user