@@ -1,14 +1,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Minimize from 'lucide-svelte/icons/minus';
|
import Minimize from 'lucide-svelte/icons/minus';
|
||||||
import Close from 'lucide-svelte/icons/x';
|
import Close from 'lucide-svelte/icons/x';
|
||||||
|
import Volume2 from 'lucide-svelte/icons/volume-2';
|
||||||
import Logo from '$assets/logo.png';
|
|
||||||
|
|
||||||
import { getCurrentWindow } from '@tauri-apps/api/window';
|
import { getCurrentWindow } from '@tauri-apps/api/window';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { launcherVersion } from '@/global';
|
import { custom_theme_audio_playing, custom_theme_volume } from '@/global';
|
||||||
import Badge from '../badge/badge.svelte';
|
import { reduceAnimations, userSettings } from '@/userSettings';
|
||||||
import { reduceAnimations } from '@/userSettings';
|
import { Volume, Volume1, VolumeOff } from 'lucide-svelte';
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const appWindow = getCurrentWindow();
|
const appWindow = getCurrentWindow();
|
||||||
@@ -21,14 +20,67 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div data-tauri-drag-region class="titlebar z-[100]">
|
<div data-tauri-drag-region class="titlebar z-[100]">
|
||||||
|
{#if $custom_theme_audio_playing}
|
||||||
|
{#if $custom_theme_audio_playing}
|
||||||
|
<div class="relative flex items-center justify-center group">
|
||||||
|
<div class="relative flex items-center justify-center p-1 cursor-pointer">
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 bg-background rounded
|
||||||
|
scale-80 opacity-0
|
||||||
|
group-hover:opacity-100
|
||||||
|
group-hover:scale-100
|
||||||
|
{$reduceAnimations ? '' : 'transition-all duration-200 ease-out'}"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
{#if $custom_theme_volume > 0.7}
|
||||||
|
<Volume2 class="z-10 pointer-events-none" size={14} />
|
||||||
|
{:else if $custom_theme_volume >= 0.15}
|
||||||
|
<Volume1 class="z-10 pointer-events-none" size={14} />
|
||||||
|
{:else if $custom_theme_volume > 0}
|
||||||
|
<Volume class="z-10 pointer-events-none" size={14} />
|
||||||
|
{:else}
|
||||||
|
<VolumeOff class="z-10 pointer-events-none" size={14} />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="absolute top-full right-0 translate-x-[7px]
|
||||||
|
opacity-0 invisible pointer-events-none -translate-y-2
|
||||||
|
group-hover:opacity-100 group-hover:visible group-hover:pointer-events-auto group-hover:translate-y-0
|
||||||
|
{$reduceAnimations ? '' : 'transition-all duration-200 ease-out'}
|
||||||
|
z-50"
|
||||||
|
>
|
||||||
|
<div class="h-2 w-full"></div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="flex items-center justify-center h-[120px] w-[35px]
|
||||||
|
bg-black/20 backdrop-blur-sm border border-white/10 rounded-xl"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min="0"
|
||||||
|
max="1"
|
||||||
|
step="0.01"
|
||||||
|
bind:value={$custom_theme_volume}
|
||||||
|
on:input={() => {
|
||||||
|
$userSettings.value('volume').set($custom_theme_volume);
|
||||||
|
$userSettings.save();
|
||||||
|
}}
|
||||||
|
class="volume-slider"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
<div class="relative flex items-center justify-center p-1 group" id="titlebar-minimize">
|
<div class="relative flex items-center justify-center p-1 group" id="titlebar-minimize">
|
||||||
<div
|
<div
|
||||||
class="absolute inset-0 bg-background rounded
|
class="absolute inset-0 bg-background rounded
|
||||||
scale-80 opacity-0
|
scale-80 opacity-0
|
||||||
group-hover:opacity-100
|
group-hover:opacity-100
|
||||||
group-hover:scale-100
|
group-hover:scale-100
|
||||||
{$reduceAnimations ? '' : 'transition-all duration-200 ease-out'}
|
{$reduceAnimations ? '' : 'transition-all duration-200 ease-out'}
|
||||||
origin-center"
|
origin-center"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<Minimize class="z-10 pointer-events-none" size={14} />
|
<Minimize class="z-10 pointer-events-none" size={14} />
|
||||||
@@ -36,11 +88,11 @@
|
|||||||
<div class="relative flex items-center justify-center p-1 group" id="titlebar-close">
|
<div class="relative flex items-center justify-center p-1 group" id="titlebar-close">
|
||||||
<div
|
<div
|
||||||
class="absolute inset-0 bg-red-500 rounded
|
class="absolute inset-0 bg-red-500 rounded
|
||||||
scale-80 opacity-0
|
scale-80 opacity-0
|
||||||
group-hover:opacity-100
|
group-hover:opacity-100
|
||||||
group-hover:scale-100
|
group-hover:scale-100
|
||||||
{$reduceAnimations ? '' : 'transition-all duration-200 ease-out'}
|
{$reduceAnimations ? '' : 'transition-all duration-200 ease-out'}
|
||||||
origin-center"
|
origin-center"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<Close class="z-10 pointer-events-none" size={14} />
|
<Close class="z-10 pointer-events-none" size={14} />
|
||||||
@@ -63,4 +115,52 @@
|
|||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.volume-slider {
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
|
||||||
|
width: 100px; /* becomes vertical */
|
||||||
|
height: 6px;
|
||||||
|
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* track */
|
||||||
|
.volume-slider::-webkit-slider-runnable-track {
|
||||||
|
height: 6px;
|
||||||
|
background: rgba(255, 255, 255, 0.25);
|
||||||
|
border-radius: 999px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* thumb */
|
||||||
|
.volume-slider::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
background: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
margin-top: -4px; /* center thumb */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Firefox */
|
||||||
|
.volume-slider::-moz-range-track {
|
||||||
|
height: 6px;
|
||||||
|
background: rgba(255, 255, 255, 0.25);
|
||||||
|
border-radius: 999px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.volume-slider::-moz-range-thumb {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
background: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import type { Component } from 'svelte';
|
|||||||
import Loading from '../screens/Loading.svelte';
|
import Loading from '../screens/Loading.svelte';
|
||||||
import type { Release } from './types';
|
import type { Release } from './types';
|
||||||
import type { Theme } from './themes';
|
import type { Theme } from './themes';
|
||||||
|
import { browser } from '$app/environment';
|
||||||
|
import { setGlobalVolume } from './utils';
|
||||||
export const currentView = writable<Component>(Loading);
|
export const currentView = writable<Component>(Loading);
|
||||||
|
|
||||||
export const platform = writable<string>('');
|
export const platform = writable<string>('');
|
||||||
@@ -27,6 +29,13 @@ export const launching = writable<boolean>(false);
|
|||||||
export const custom_themes = writable<Theme[]>([]);
|
export const custom_themes = writable<Theme[]>([]);
|
||||||
export const custom_theme_container = writable<HTMLElement | undefined>(undefined);
|
export const custom_theme_container = writable<HTMLElement | undefined>(undefined);
|
||||||
export const active_custom_theme = writable<Theme | undefined>(undefined);
|
export const active_custom_theme = writable<Theme | undefined>(undefined);
|
||||||
|
export const custom_theme_audio_playing = writable<boolean>(false);
|
||||||
|
export const custom_theme_volume = writable<number>(0.15);
|
||||||
|
custom_theme_volume.subscribe((val) => {
|
||||||
|
if (browser) {
|
||||||
|
setGlobalVolume(val);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export const serverPing = writable<number | undefined>(undefined);
|
export const serverPing = writable<number | undefined>(undefined);
|
||||||
export const serverConnectionFails = writable(0);
|
export const serverConnectionFails = writable(0);
|
||||||
|
|||||||
+2
-2
@@ -269,7 +269,7 @@ export const downloadTheme = async (theme: Theme, force = false): Promise<boolea
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const loadTheme = async (theme: Theme, themeContainer: HTMLElement) => {
|
export const loadTheme = async (theme: Theme, themeContainer: HTMLElement, volume = 0.15) => {
|
||||||
active_custom_theme.set(theme);
|
active_custom_theme.set(theme);
|
||||||
themeContainer.innerHTML = '';
|
themeContainer.innerHTML = '';
|
||||||
if (theme.scriptUrl.length > 0) {
|
if (theme.scriptUrl.length > 0) {
|
||||||
@@ -277,7 +277,7 @@ export const loadTheme = async (theme: Theme, themeContainer: HTMLElement) => {
|
|||||||
/* @vite-ignore */
|
/* @vite-ignore */
|
||||||
themeScript.mountTheme(themeContainer, { assets: theme.assets });
|
themeScript.mountTheme(themeContainer, { assets: theme.assets });
|
||||||
}
|
}
|
||||||
setGlobalVolume(0.15);
|
setGlobalVolume(volume);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deleteTheme = async (themeToUninstall: Theme) => {
|
export const deleteTheme = async (themeToUninstall: Theme) => {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
import { type ClassValue, clsx } from 'clsx';
|
import { type ClassValue, clsx } from 'clsx';
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
import { custom_theme_audio_playing } from './global';
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
return twMerge(clsx(inputs));
|
return twMerge(clsx(inputs));
|
||||||
@@ -144,3 +145,47 @@ export function fadeGlobalVolume(volumeFrom: number, volumeTo: number, duration:
|
|||||||
|
|
||||||
animate();
|
animate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function trackMediaContainer(node: HTMLElement) {
|
||||||
|
const media = new Set<HTMLMediaElement>();
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
custom_theme_audio_playing.set(Array.from(media).some((el) => !el.paused));
|
||||||
|
}
|
||||||
|
|
||||||
|
function register(el: HTMLMediaElement) {
|
||||||
|
media.add(el);
|
||||||
|
|
||||||
|
const handler = () => update();
|
||||||
|
el.addEventListener('play', handler);
|
||||||
|
el.addEventListener('pause', handler);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
media.delete(el);
|
||||||
|
el.removeEventListener('play', handler);
|
||||||
|
el.removeEventListener('pause', handler);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// find existing media inside the div
|
||||||
|
node.querySelectorAll<HTMLMediaElement>('video, audio').forEach(register);
|
||||||
|
|
||||||
|
const observer = new MutationObserver((mutations) => {
|
||||||
|
for (const m of mutations) {
|
||||||
|
m.addedNodes.forEach((n) => {
|
||||||
|
if (n instanceof HTMLMediaElement) register(n);
|
||||||
|
if (n instanceof HTMLElement) {
|
||||||
|
n.querySelectorAll<HTMLMediaElement>('video, audio').forEach(register);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(node, { childList: true, subtree: true });
|
||||||
|
|
||||||
|
return {
|
||||||
|
destroy() {
|
||||||
|
observer.disconnect();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
active_custom_theme,
|
active_custom_theme,
|
||||||
currentLoadingInfo,
|
currentLoadingInfo,
|
||||||
custom_theme_container,
|
custom_theme_container,
|
||||||
|
custom_theme_volume,
|
||||||
custom_themes,
|
custom_themes,
|
||||||
discordPresence,
|
discordPresence,
|
||||||
firstStartup,
|
firstStartup,
|
||||||
@@ -41,6 +42,7 @@
|
|||||||
import { sileo } from 'sileo';
|
import { sileo } from 'sileo';
|
||||||
import AnimatedBg from '@/components/ui/animated-bg/AnimatedBg.svelte';
|
import AnimatedBg from '@/components/ui/animated-bg/AnimatedBg.svelte';
|
||||||
import { SemVer } from 'semver';
|
import { SemVer } from 'semver';
|
||||||
|
import { trackMediaContainer } from '@/utils';
|
||||||
|
|
||||||
let { children } = $props();
|
let { children } = $props();
|
||||||
|
|
||||||
@@ -107,14 +109,15 @@
|
|||||||
$userAuth.init();
|
$userAuth.init();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const themeVolume = $userSettings.value('volume').get(0.15);
|
||||||
|
console.log(themeVolume);
|
||||||
|
custom_theme_volume.set(themeVolume);
|
||||||
currentLoadingInfo.set('Loading themes...');
|
currentLoadingInfo.set('Loading themes...');
|
||||||
const downloadableThemes = await getDownloadableThemes();
|
const downloadableThemes = await getDownloadableThemes();
|
||||||
const themes = await getThemes();
|
const themes = await getThemes();
|
||||||
const combinedThemes = [...themes];
|
const combinedThemes = [...themes];
|
||||||
for (const theme of downloadableThemes) {
|
for (const theme of downloadableThemes) {
|
||||||
if (!combinedThemes.find((t) => t.name === theme.name)) combinedThemes.push(theme);
|
if (!combinedThemes.find((t) => t.name === theme.name)) combinedThemes.push(theme);
|
||||||
|
|
||||||
//set updateAvailable to true if downloadableTheme version is higher than installed theme version
|
|
||||||
const installedTheme = themes.find((t) => t.name === theme.name);
|
const installedTheme = themes.find((t) => t.name === theme.name);
|
||||||
if (installedTheme) {
|
if (installedTheme) {
|
||||||
const installedThemeVersion = new SemVer(installedTheme.version);
|
const installedThemeVersion = new SemVer(installedTheme.version);
|
||||||
@@ -133,9 +136,9 @@
|
|||||||
if (!theme) {
|
if (!theme) {
|
||||||
$userSettings.value('theme').set('Default');
|
$userSettings.value('theme').set('Default');
|
||||||
await $userSettings.save();
|
await $userSettings.save();
|
||||||
loadTheme(themes[0], $custom_theme_container!);
|
loadTheme(themes[0], $custom_theme_container!, themeVolume);
|
||||||
} else {
|
} else {
|
||||||
loadTheme(theme, $custom_theme_container!);
|
loadTheme(theme, $custom_theme_container!, themeVolume);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
sileo.error({
|
sileo.error({
|
||||||
@@ -230,6 +233,7 @@
|
|||||||
? 'hidden'
|
? 'hidden'
|
||||||
: 'absolute z-0 top-0 left-0 w-full h-full object-cover object-center aspect-video'}
|
: 'absolute z-0 top-0 left-0 w-full h-full object-cover object-center aspect-video'}
|
||||||
bind:this={$custom_theme_container}
|
bind:this={$custom_theme_container}
|
||||||
|
use:trackMediaContainer
|
||||||
></div>
|
></div>
|
||||||
{#if ($active_custom_theme && $active_custom_theme.name === 'Default') || !$active_custom_theme}
|
{#if ($active_custom_theme && $active_custom_theme.name === 'Default') || !$active_custom_theme}
|
||||||
<AnimatedBg />
|
<AnimatedBg />
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
beatmapSets,
|
beatmapSets,
|
||||||
currentSkin,
|
currentSkin,
|
||||||
custom_theme_container,
|
custom_theme_container,
|
||||||
|
custom_theme_volume,
|
||||||
custom_themes,
|
custom_themes,
|
||||||
discordPresence,
|
discordPresence,
|
||||||
launcherStream,
|
launcherStream,
|
||||||
@@ -1564,7 +1565,7 @@
|
|||||||
onclick={async () => {
|
onclick={async () => {
|
||||||
if (theme.status === 'installed') {
|
if (theme.status === 'installed') {
|
||||||
if ($custom_theme_container) {
|
if ($custom_theme_container) {
|
||||||
loadTheme(theme, $custom_theme_container);
|
loadTheme(theme, $custom_theme_container, $custom_theme_volume);
|
||||||
$userSettings.value('theme').set(theme.name);
|
$userSettings.value('theme').set(theme.name);
|
||||||
$userSettings.save();
|
$userSettings.save();
|
||||||
} else {
|
} else {
|
||||||
@@ -1607,7 +1608,7 @@
|
|||||||
onclick={async () => {
|
onclick={async () => {
|
||||||
const defaultTheme = $custom_themes.find((t) => t.name === 'Default');
|
const defaultTheme = $custom_themes.find((t) => t.name === 'Default');
|
||||||
if (defaultTheme) {
|
if (defaultTheme) {
|
||||||
loadTheme(defaultTheme, $custom_theme_container!);
|
loadTheme(defaultTheme, $custom_theme_container!, $custom_theme_volume);
|
||||||
$userSettings.value('theme').set(defaultTheme.name);
|
$userSettings.value('theme').set(defaultTheme.name);
|
||||||
$userSettings.save();
|
$userSettings.save();
|
||||||
|
|
||||||
|
|||||||
@@ -131,7 +131,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!$firstStartup) {
|
if (!$firstStartup) {
|
||||||
/* const launcherTheme = $userSettings.value('launcherTheme').get('default');
|
/* const launcherTheme = $userSettings.value('launcherTheme').get('default');
|
||||||
const them = THEMES.find((theme) => theme.name === launcherTheme);
|
const them = THEMES.find((theme) => theme.name === launcherTheme);
|
||||||
if (them) theme.set(them); */
|
if (them) theme.set(them); */
|
||||||
currentLoadingInfo.set('Checking osu installation path...');
|
currentLoadingInfo.set('Checking osu installation path...');
|
||||||
|
|||||||
Reference in New Issue
Block a user