feat: add cursor settings, enhance setup wizard and loading animations

This commit is contained in:
HorizonCode 2025-07-01 14:13:40 +02:00
parent ba211641a6
commit 50f6812054
10 changed files with 374 additions and 130 deletions

View File

@ -21,6 +21,7 @@
"osu-classes": "3.1.0", "osu-classes": "3.1.0",
"osu-parsers": "4.1.7", "osu-parsers": "4.1.7",
"radix-icons-svelte": "1.2.1", "radix-icons-svelte": "1.2.1",
"svelte-confetti": "^2.0.0",
}, },
"devDependencies": { "devDependencies": {
"@lucide/svelte": "^0.482.0", "@lucide/svelte": "^0.482.0",
@ -557,6 +558,8 @@
"svelte-check": ["svelte-check@4.2.2", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-1+31EOYZ7NKN0YDMKusav2hhEoA51GD9Ws6o//0SphMT0ve9mBTsTUEX7OmDMadUP3KjNHsSKtJrqdSaD8CrGQ=="], "svelte-check": ["svelte-check@4.2.2", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-1+31EOYZ7NKN0YDMKusav2hhEoA51GD9Ws6o//0SphMT0ve9mBTsTUEX7OmDMadUP3KjNHsSKtJrqdSaD8CrGQ=="],
"svelte-confetti": ["svelte-confetti@2.3.1", "", { "peerDependencies": { "svelte": ">=5.0.0" } }, "sha512-bKd8etTOeBQyeS9LDPuSd7Oqy5msf0xvxItzsHPajKaarr/LWFzqPq7rp6QQO5rGTzLgM0fmjovOvLkRbrd2gg=="],
"svelte-toolbelt": ["svelte-toolbelt@0.7.1", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.23.2", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ=="], "svelte-toolbelt": ["svelte-toolbelt@0.7.1", "", { "dependencies": { "clsx": "^2.1.1", "runed": "^0.23.2", "style-to-object": "^1.0.8" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ=="],
"sync-child-process": ["sync-child-process@1.0.2", "", { "dependencies": { "sync-message-port": "^1.0.0" } }, "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA=="], "sync-child-process": ["sync-child-process@1.0.2", "", { "dependencies": { "sync-message-port": "^1.0.0" } }, "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA=="],

View File

@ -32,7 +32,8 @@
"lucide-svelte": "0.523.0", "lucide-svelte": "0.523.0",
"osu-classes": "3.1.0", "osu-classes": "3.1.0",
"osu-parsers": "4.1.7", "osu-parsers": "4.1.7",
"radix-icons-svelte": "1.2.1" "radix-icons-svelte": "1.2.1",
"svelte-confetti": "^2.0.0"
}, },
"devDependencies": { "devDependencies": {
"@lucide/svelte": "^0.482.0", "@lucide/svelte": "^0.482.0",

20
src/lib/displayUtils.ts Normal file
View File

@ -0,0 +1,20 @@
export function estimateRefreshRate(): Promise<number> {
return new Promise((resolve) => {
let last = performance.now();
let frames = 0;
function loop() {
const now = performance.now();
frames++;
if (now - last >= 1000) {
console.log(`Estimated Refresh Rate: ${frames} FPS`);
resolve(frames); // estimated Hz
} else {
requestAnimationFrame(loop);
}
}
requestAnimationFrame(loop);
});
}

View File

@ -4,6 +4,7 @@ import type { Component } from 'svelte';
import Loading from '../pages/Loading.svelte'; import Loading from '../pages/Loading.svelte';
export const current_view = writable<Component>(Loading); export const current_view = writable<Component>(Loading);
export const first_startup = writable<boolean>(false);
export const server_ping = writable<number | undefined>(undefined); export const server_ping = writable<number | undefined>(undefined);
export const server_connection_fails = writable(0); export const server_connection_fails = writable(0);

View File

@ -1,8 +1,11 @@
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
import { Config } from './config'; import { Config } from './config';
export const userSettings = writable<Config>(new Config()); export const userSettings = writable<Config>(new Config(false));
export const customCursor = writable<boolean>(true); export const customCursor = writable<boolean>(true);
export const cursorSmoothening = writable<boolean>(true); export const cursorSmoothening = writable<boolean>(true);
export const cursorSmoothness = writable<number>(180);
export const reduceAnimations = writable<boolean>(false); export const reduceAnimations = writable<boolean>(false);
export const osuInstallationPath = writable<string>('');

View File

@ -27,7 +27,7 @@
import Label from '@/components/ui/label/label.svelte'; import Label from '@/components/ui/label/label.svelte';
import { cursorSmoothening, customCursor, reduceAnimations, userSettings } from '@/userSettings'; import { cursorSmoothening, customCursor, reduceAnimations, userSettings } from '@/userSettings';
let selectedTab = $state('settings'); let selectedTab = $state('home');
let launching = $state(false); let launching = $state(false);
</script> </script>
@ -264,7 +264,11 @@
</div> </div>
<div <div
class="mt-auto bg-theme-900/90 border border-theme-800/90 rounded-lg px-6 py-3" class="mt-auto bg-theme-900/90 border border-theme-800/90 rounded-lg px-6 py-3"
in:scale={{ duration: $reduceAnimations ? 0 : 400, start: 0.98 }} in:scale={{
duration: $reduceAnimations ? 0 : 400,
delay: $reduceAnimations ? 0 : 50,
start: 0.98,
}}
> >
<div class="flex flex-row items-center gap-2"> <div class="flex flex-row items-center gap-2">
<Gamepad2 class="text-muted-foreground" size="24" /> <Gamepad2 class="text-muted-foreground" size="24" />
@ -336,6 +340,76 @@
class="flex items-center justify-center w-5 h-5" class="flex items-center justify-center w-5 h-5"
></Checkbox> ></Checkbox>
<div class="flex flex-col">
<Label class="text-sm" for="setting-cursor-smoothening">Reduce Animations</Label>
<div class="text-muted-foreground text-xs">
Disables some animations in the Launcher to improve performance on low-end devices.
</div>
</div>
<Checkbox
id="setting-cursor-smoothening"
checked={$reduceAnimations}
onCheckedChange={async (e) => {
reduceAnimations.set(e);
$userSettings.save();
}}
disabled={!$customCursor}
class="flex items-center justify-center w-5 h-5"
></Checkbox>
</div>
</div>
<div
class="bg-theme-900/90 flex flex-col justify-center gap-3 border border-theme-800/90 rounded-lg"
in:scale={{
duration: $reduceAnimations ? 0 : 400,
delay: $reduceAnimations ? 0 : 50,
start: 0.98,
}}
>
<div class="flex flex-row items-center gap-3 font-semibold text-xl px-3 pt-3">
<Settings2 /> EZPPLauncher Settings
</div>
<div
class="grid grid-cols-[1fr_auto] gap-y-5 items-center border-t border-theme-800 py-3 px-6"
>
<div class="flex flex-col">
<Label class="text-sm" for="setting-custom-cursor">Lazer-Style Cursor</Label>
<div class="text-muted-foreground text-xs">
Enable a custom cursor in the Launcher like in the lazer build of osu!
</div>
</div>
<Checkbox
id="setting-custom-cursor"
checked={$customCursor}
onCheckedChange={async (e) => {
if (!e) {
cursorSmoothening.set(false);
}
customCursor.set(e);
$userSettings.save();
}}
class="flex items-center justify-center w-5 h-5"
></Checkbox>
<div class="flex flex-col">
<Label class="text-sm" for="setting-cursor-smoothening">Cursor Smoothening</Label>
<div class="text-muted-foreground text-xs">
Makes the custom cursor movement smoother.
</div>
</div>
<Checkbox
id="setting-cursor-smoothening"
checked={$cursorSmoothening}
onCheckedChange={async (e) => {
if (!$customCursor) return;
cursorSmoothening.set(e);
$userSettings.save();
}}
disabled={!$customCursor}
class="flex items-center justify-center w-5 h-5"
></Checkbox>
<div class="flex flex-col"> <div class="flex flex-col">
<Label class="text-sm" for="setting-cursor-smoothening">Reduce Animations</Label> <Label class="text-sm" for="setting-cursor-smoothening">Reduce Animations</Label>
<div class="text-muted-foreground text-xs"> <div class="text-muted-foreground text-xs">

View File

@ -1,7 +1,12 @@
<script lang="ts"> <script lang="ts">
import Logo from '$assets/logo.png'; import Logo from '$assets/logo.png';
import { animate } from 'animejs'; import { estimateRefreshRate } from '@/displayUtils';
import { current_view, first_startup } from '@/global';
import { cursorSmoothness } from '@/userSettings';
import { animate, utils } from 'animejs';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import SetupWizard from './SetupWizard.svelte';
import Launch from './Launch.svelte';
let ezppLogo: HTMLImageElement; let ezppLogo: HTMLImageElement;
let spinnerCircle: SVGCircleElement; let spinnerCircle: SVGCircleElement;
@ -26,25 +31,61 @@
}, 450); }, 450);
}; };
// Animate logo on mount: pop-in, then pulse const calculateCursorSmoothness = async () => {
const refreshRate = await estimateRefreshRate();
const hzMin = 60;
const hzMax = 144;
const durationMin = 70;
const durationMax = 180;
const duration =
durationMin + ((refreshRate - hzMin) / (hzMax - hzMin)) * (durationMax - durationMin);
cursorSmoothness.set(Math.round(duration));
};
const prepare = async () => {
await calculateCursorSmoothness();
animate(ezppLogo, {
opacity: [1, 0],
scale: [1, 1.05],
duration: 1000,
ease: (t: number) => (t - 1) ** 7 + 1,
onComplete: () => {},
});
animate(spinnerCircle, {
opacity: 0,
duration: 1000,
ease: (t: number) => (t - 1) ** 7 + 1,
onComplete: () => {},
});
setTimeout(() => {
if ($first_startup) current_view.set(SetupWizard);
else current_view.set(Launch);
}, 250);
};
onMount(() => { onMount(() => {
// Logo pop-in and pulse
animate(ezppLogo, { animate(ezppLogo, {
opacity: [0, 1], opacity: [0, 1],
scale: [0, 1], scale: [0.95, 1],
duration: 900, duration: 900,
ease: (t: number) => Math.pow(2, -5 * t) * Math.sin((t - 0.075) * 20.94) + 1 - 0.0005 * t, ease: (t: number) => (t - 1) ** 7 + 1,
onComplete: doBPMAnimation, onComplete: doBPMAnimation,
}); });
// Spinner animation (seamless, starts at 12 o'clock) animate(spinnerCircle, {
if (spinnerCircle) { strokeDashoffset: [0, -565],
animate(spinnerCircle, { duration: 1800,
strokeDashoffset: [0, -565], easing: 'linear',
duration: 1800, loop: true,
easing: 'linear', });
loop: true,
}); prepare();
}
return () => {
utils.remove(ezppLogo);
utils.remove(spinnerCircle);
};
}); });
</script> </script>

View File

@ -4,10 +4,16 @@
import Input from '@/components/ui/input/input.svelte'; import Input from '@/components/ui/input/input.svelte';
import { animate } from 'animejs'; import { animate } from 'animejs';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { fade } from 'svelte/transition'; import { fade, scale } from 'svelte/transition';
import { invoke } from '@tauri-apps/api/core'; import { invoke } from '@tauri-apps/api/core';
import { Check, CheckCircle, CircleOff } from 'lucide-svelte'; import { Check, CheckCircle, CircleOff, Settings2 } from 'lucide-svelte';
import { open } from '@tauri-apps/plugin-dialog'; import { open } from '@tauri-apps/plugin-dialog';
import Checkbox from '@/components/ui/checkbox/checkbox.svelte';
import { cursorSmoothening, customCursor, reduceAnimations, userSettings } from '@/userSettings';
import Label from '@/components/ui/label/label.svelte';
import { current_view } from '@/global';
import Launch from './Launch.svelte';
import Confetti from 'svelte-confetti';
let selectedStep = $state(1); let selectedStep = $state(1);
const steps = ['Welcome', 'Locate your osu! Installation', 'Appearance Settings']; const steps = ['Welcome', 'Locate your osu! Installation', 'Appearance Settings'];
@ -16,6 +22,7 @@
let manualSelect = $state(false); let manualSelect = $state(false);
let manualSelectValid = $state(false); let manualSelectValid = $state(false);
let autoDetectedOsuPath = $state(false); let autoDetectedOsuPath = $state(false);
let wizardFinished = $state(false);
let ezppLogo: HTMLImageElement | undefined = $state(undefined); let ezppLogo: HTMLImageElement | undefined = $state(undefined);
@ -57,6 +64,7 @@
osuInstallationPath = selectedPath; osuInstallationPath = selectedPath;
autoDetectedOsuPath = false; autoDetectedOsuPath = false;
manualSelectValid = true; manualSelectValid = true;
$userSettings.value('osu_installation_path').set(osuInstallationPath);
} }
}; };
@ -66,131 +74,223 @@
if (osuInstallPath) { if (osuInstallPath) {
osuInstallationPath = osuInstallPath; osuInstallationPath = osuInstallPath;
autoDetectedOsuPath = true; autoDetectedOsuPath = true;
$userSettings.value('osu_installation_path').set(osuInstallationPath);
} }
}); });
</script> </script>
<div class="grid grid-cols-[0.41fr_1fr] mt-[50px] h-[calc(100vh-50px)]"> {#if wizardFinished}
<div class="w-full h-full border-r border-theme-800/90 flex flex-col gap-6 p-3"> <div class="relative flex flex-col items-center justify-center mt-[50px] h-[calc(100vh-50px)]">
{#each steps as step, i (step)} <div class="absolute h-fit w-fit top-1/2 left-1/2">
<div <Confetti amount={200} y={[-1, 1.5]} x={[-2.3, 2.3]} colorArray={['#C6A0F3']} />
class="flex flex-row items-center gap-2 border {selectedStep === i + 1 </div>
? 'border-primary-800/30 bg-primary-900/30' <h1 class="text-3xl font-semibold">EZPPLauncher Setup completed!</h1>
: selectedStep > i <p class="text-muted-foreground mt-2">You are now ready to farm some maps!</p>
? 'border-green-800/30 bg-green-900/30' <Button
: 'border-theme-800 bg-theme-900'} rounded-lg p-2 transition-all" class="mt-4"
> onclick={async () => {
<div await $userSettings.save();
class="flex flex-col items-center justify-center h-8 w-8 border-[2px] {selectedStep > i + 1 ? "border-green-600" : "border-theme-600"} rounded-full" current_view.set(Launch);
> }}
{#if selectedStep > i + 1} >
<Check class="mt-0.5 text-green-400" /> Finish
{:else} </Button>
<span class="text-lg font-semibold text-theme-100">{i + 1}</span>
{/if}
</div>
<span
class="{selectedStep === i + 1
? 'text-white'
: selectedStep > i
? 'text-green-500'
: "'text-muted-foreground'"} transition-all text-sm font-bold">{step}</span
>
</div>
{/each}
</div> </div>
<div class="flex flex-col gap-6 w-full h-full bg-theme-900/40 p-6"> {:else}
{#if selectedStep === 1} <div class="grid grid-cols-[0.41fr_1fr] mt-[50px] h-[calc(100vh-50px)]">
<div <div class="w-full h-full border-r border-theme-800/90 flex flex-col gap-6 p-3">
class="my-auto h-full w-full bg-theme-800/15 rounded-lg border border-900/60 p-6 flex flex-col items-center justify-center" {#each steps as step, i (step)}
in:fade={{ duration: 100 }} <div
> class="flex flex-row items-center gap-2 border {selectedStep === i + 1
<img ? 'border-primary-800/30 bg-primary-900/30'
src={Logo} : selectedStep > i
alt="EZPPLauncher Logo" ? 'border-green-800/30 bg-green-900/30'
class="w-52 h-52 mb-2" : 'border-theme-800 bg-theme-900'} rounded-lg p-2 transition-all"
bind:this={ezppLogo} >
onmouseenter={logo_mouseenter} <div
onmouseleave={logo_mouseleave} class="flex flex-col items-center justify-center h-8 w-8 border-[2px] {selectedStep >
/> i + 1
<h1 class="text-3xl font-semibold">Welcome to EZPPLauncher!</h1> ? 'border-green-600'
<p class="text-muted-foreground mt-2"> : 'border-theme-600'} rounded-full"
This setup wizard will guide you through the initial setup of EZPPLauncher. >
</p> {#if selectedStep > i + 1}
<div class="bg-red-800/20 border border-red-900/20 text-red-500 p-4 rounded-lg mt-4"> <Check class="mt-0.5 text-green-400" />
Please make sure you have osu! installed on your system before proceeding. {:else}
</div> <span class="text-lg font-semibold text-theme-100">{i + 1}</span>
</div> {/if}
{:else if selectedStep === 2} </div>
<div <span
class="my-auto h-full w-full bg-theme-800/15 rounded-lg border border-900/60 p-6 flex flex-col items-center justify-center" class="{selectedStep === i + 1
in:fade={{ duration: 100 }} ? 'text-white'
> : selectedStep > i
<h1 class="text-3xl font-semibold">Locate your osu! Installation</h1> ? 'text-green-500'
<p class="text-muted-foreground mt-2"> : "'text-muted-foreground'"} transition-all text-sm font-bold">{step}</span
Please select the folder where your osu! installation is located.
</p>
<div class="flex flex-row w-full">
<Input
class="mt-4 w-full bg-theme-950 border-theme-800 border-r-0 rounded-r-none"
type="text"
placeholder="Path to osu! installation"
value={osuInstallationPath}
/>
<Button
class="mt-4 bg-theme-950 border-theme-800 rounded-l-none"
variant="outline"
onclick={browse_osu_installation}>Browse</Button
> >
</div> </div>
{#if !manualSelect} {/each}
{#if autoDetectedOsuPath} </div>
<div class="flex flex-col gap-6 w-full h-full bg-theme-900/40 p-6">
{#if selectedStep === 1}
<div
class="my-auto h-full w-full bg-theme-800/15 rounded-lg border border-900/60 p-6 flex flex-col items-center justify-center"
in:fade={{ duration: $reduceAnimations ? 0 : 200 }}
>
<img
src={Logo}
alt="EZPPLauncher Logo"
class="w-52 h-52 mb-2"
bind:this={ezppLogo}
onmouseenter={logo_mouseenter}
onmouseleave={logo_mouseleave}
/>
<h1 class="text-3xl font-semibold">Welcome to EZPPLauncher!</h1>
<p class="text-muted-foreground mt-2">
This setup wizard will guide you through the initial setup of EZPPLauncher.
</p>
<div class="bg-red-800/20 border border-red-900/20 text-red-500 p-4 rounded-lg mt-4">
Please make sure you have osu! installed on your system before proceeding.
</div>
</div>
{:else if selectedStep === 2}
<div
class="my-auto h-full w-full bg-theme-800/15 rounded-lg border border-900/60 p-6 flex flex-col items-center justify-center"
in:fade={{ duration: $reduceAnimations ? 0 : 200 }}
>
<h1 class="text-3xl font-semibold">Locate your osu! Installation</h1>
<p class="text-muted-foreground mt-2">
Please select the folder where your osu! installation is located.
</p>
<div class="flex flex-row w-full">
<Input
class="mt-4 w-full bg-theme-950 border-theme-800 border-r-0 rounded-r-none"
type="text"
placeholder="Path to osu! installation"
value={osuInstallationPath}
/>
<Button
class="mt-4 bg-theme-950 border-theme-800 rounded-l-none"
variant="outline"
onclick={browse_osu_installation}>Browse</Button
>
</div>
{#if !manualSelect}
{#if autoDetectedOsuPath}
<div
class="flex flex-row gap-3 bg-green-800/20 border border-green-900/20 text-green-500 p-4 rounded-lg mt-4"
>
<CheckCircle />
<span>Auto-detected osu! installation path! Please check if its correct!</span>
</div>
{:else}
<div
class="flex flex-row gap-3 bg-red-800/20 border border-red-900/20 text-red-500 p-4 rounded-lg mt-4"
>
<CircleOff />
<span>Could not auto-detect osu! installation path. Please select it manually.</span
>
</div>
{/if}
{:else if manualSelectValid}
<div <div
class="flex flex-row gap-3 bg-green-800/20 border border-green-900/20 text-green-500 p-4 rounded-lg mt-4" class="flex flex-row gap-3 bg-green-800/20 border border-green-900/20 text-green-500 p-4 rounded-lg mt-4"
> >
<CheckCircle /> <CheckCircle />
<span>Auto-detected osu! installation path! Please check if its correct!</span> <span>Selected osu! installation path is valid!</span>
</div> </div>
{:else} {:else}
<div <div
class="flex flex-row gap-3 bg-red-800/20 border border-red-900/20 text-red-500 p-4 rounded-lg mt-4" class="flex flex-row gap-3 bg-red-800/20 border border-red-900/20 text-red-500 p-4 rounded-lg mt-4"
> >
<CircleOff /> <CircleOff />
<span>Could not auto-detect osu! installation path. Please select it manually.</span> <span
>Selected osu! installation path is invalid! Please select a valid osu!
installation.</span
>
</div> </div>
{/if} {/if}
{:else if manualSelectValid} </div>
<div {:else if selectedStep === 3}
class="flex flex-row gap-3 bg-green-800/20 border border-green-900/20 text-green-500 p-4 rounded-lg mt-4" <div
> class="bg-theme-900/90 flex flex-col justify-center gap-3 border border-theme-800/90 rounded-lg"
<CheckCircle /> in:fade={{ duration: $reduceAnimations ? 0 : 200 }}
<span>Selected osu! installation path is valid!</span> >
<div class="flex flex-row items-center gap-3 font-semibold text-xl px-3 pt-3">
<Settings2 /> EZPPLauncher Settings
</div> </div>
{:else}
<div <div
class="flex flex-row gap-3 bg-red-800/20 border border-red-900/20 text-red-500 p-4 rounded-lg mt-4" class="grid grid-cols-[1fr_auto] gap-y-5 items-center border-t border-theme-800 py-3 px-6"
> >
<CircleOff /> <div class="flex flex-col">
<span <Label class="text-sm" for="setting-custom-cursor">Lazer-Style Cursor</Label>
>Selected osu! installation path is invalid! Please select a valid osu! installation.</span <div class="text-muted-foreground text-xs">
> Enable a custom cursor in the Launcher like in the lazer build of osu!
</div> </div>
{/if} </div>
</div> <Checkbox
{/if} id="setting-custom-cursor"
checked={$customCursor}
onCheckedChange={async (e) => {
if (!e) {
cursorSmoothening.set(false);
}
customCursor.set(e);
}}
class="flex items-center justify-center w-5 h-5"
></Checkbox>
<div class="mt-auto flex flex-row items-center justify-between"> <div class="flex flex-col">
<Button <Label class="text-sm" for="setting-cursor-smoothening">Cursor Smoothening</Label>
class="bg-theme-950 hover:bg-theme-800" <div class="text-muted-foreground text-xs">
variant="outline" Makes the custom cursor movement smoother.
onclick={() => (selectedStep = Math.max(selectedStep - 1, 1))} </div>
disabled={selectedStep <= 1}>Previous</Button </div>
> <Checkbox
<Button id="setting-cursor-smoothening"
onclick={() => (selectedStep = Math.min(selectedStep + 1, steps.length))} checked={$cursorSmoothening}
disabled={selectedStep >= steps.length || onCheckedChange={async (e) => {
(selectedStep === 2 && osuInstallationPath.length <= 0)}>Next</Button if (!$customCursor) return;
> cursorSmoothening.set(e);
}}
disabled={!$customCursor}
class="flex items-center justify-center w-5 h-5"
></Checkbox>
<div class="flex flex-col">
<Label class="text-sm" for="setting-cursor-smoothening">Reduce Animations</Label>
<div class="text-muted-foreground text-xs">
Disables some animations in the Launcher to improve performance on low-end devices.
</div>
</div>
<Checkbox
id="setting-cursor-smoothening"
checked={$reduceAnimations}
onCheckedChange={async (e) => {
reduceAnimations.set(e);
}}
disabled={!$customCursor}
class="flex items-center justify-center w-5 h-5"
></Checkbox>
</div>
</div>
{/if}
<div class="mt-auto flex flex-row items-center justify-between">
<Button
class="bg-theme-950 hover:bg-theme-800"
variant="outline"
onclick={() => (selectedStep = Math.max(selectedStep - 1, 1))}
disabled={selectedStep <= 1}>Previous</Button
>
<Button
onclick={() => {
if (selectedStep >= steps.length) wizardFinished = true;
else selectedStep = Math.min(selectedStep + 1, steps.length);
}}
disabled={selectedStep > steps.length ||
(selectedStep === 2 && osuInstallationPath.length <= 0)}
>{selectedStep >= steps.length ? 'Finish' : 'Next'}</Button
>
</div>
</div> </div>
</div> </div>
</div> {/if}

View File

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import Titlebar from '@/components/ui/titlebar/titlebar.svelte'; import Titlebar from '@/components/ui/titlebar/titlebar.svelte';
import '../app.css'; import '../app.css';
import { current_view, setupValues } from '@/global'; import { current_view, first_startup, setupValues } from '@/global';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import OsuCursor from '@/components/ui/osu-cursor/OsuCursor.svelte'; import OsuCursor from '@/components/ui/osu-cursor/OsuCursor.svelte';
import { cursorSmoothening, customCursor, reduceAnimations, userSettings } from '@/userSettings'; import { cursorSmoothening, customCursor, reduceAnimations, userSettings } from '@/userSettings';
@ -27,11 +27,7 @@
cursorSmoothening.subscribe((val) => config_cursor_smoothening.set(val)); cursorSmoothening.subscribe((val) => config_cursor_smoothening.set(val));
reduceAnimations.subscribe((val) => config_reduce_animations.set(val)); reduceAnimations.subscribe((val) => config_reduce_animations.set(val));
if (!firstStartup) { first_startup.set(firstStartup);
current_view.set(Launch);
return;
}
/* current_view.set(SetupWizard); */
}); });
</script> </script>

View File

@ -1,7 +1,12 @@
<script lang="ts"> <script lang="ts">
import { current_view } from '@/global'; import { current_view } from '@/global';
import { fade } from 'svelte/transition';
const View = $derived($current_view); const View = $derived($current_view);
</script> </script>
<View /> {#key View}
<div in:fade={{ duration: 300 }}>
<View />
</div>
{/key}