Merge branch 'themes-v2' of https://git.ez-pp.farm/EZPPFarm/EZPPLauncher into themes-v2
This commit is contained in:
5 files changed
+113
-9
No files matched your search
+32
-1
@@ -162,7 +162,10 @@
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
html {
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
body {
|
||||
@@ -197,3 +200,31 @@ video::-webkit-media-controls-overlay-enclosure {
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
}
|
||||
|
||||
/* Crossfade the rendered page when switching themes via the View Transitions API. */
|
||||
::view-transition-old(root) {
|
||||
animation: 320ms cubic-bezier(0.4, 0, 0.2, 1) both theme-crossfade-out;
|
||||
}
|
||||
|
||||
::view-transition-new(root) {
|
||||
animation: 320ms cubic-bezier(0.4, 0, 0.2, 1) both theme-crossfade-in;
|
||||
}
|
||||
|
||||
@keyframes theme-crossfade-out {
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes theme-crossfade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
::view-transition-old(root),
|
||||
::view-transition-new(root) {
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,34 @@
|
||||
|
||||
const iconSet = $derived($active_custom_theme?.definition?.iconSet ?? DEFAULT_THEME_ICON_SET);
|
||||
|
||||
let volumeOpen = $state(false);
|
||||
let volumeDragging = $state(false);
|
||||
let volumeControl: HTMLDivElement | undefined = $state();
|
||||
|
||||
function openVolume() {
|
||||
volumeOpen = true;
|
||||
}
|
||||
|
||||
function closeVolume() {
|
||||
if (!volumeDragging) volumeOpen = false;
|
||||
}
|
||||
|
||||
function startVolumeDrag(event: PointerEvent) {
|
||||
if (event.button !== 0) return;
|
||||
volumeDragging = true;
|
||||
volumeOpen = true;
|
||||
}
|
||||
|
||||
function endVolumeDrag(event: PointerEvent) {
|
||||
if (!volumeDragging) return;
|
||||
volumeDragging = false;
|
||||
|
||||
const element = document.elementFromPoint(event.clientX, event.clientY);
|
||||
if (!element || !volumeControl?.contains(element)) {
|
||||
volumeOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
const appWindow = getCurrentWindow();
|
||||
|
||||
@@ -26,9 +54,18 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:window onpointerup={endVolumeDrag} onpointercancel={endVolumeDrag} />
|
||||
|
||||
<div data-tauri-drag-region class="titlebar z-[100]" class:macos-window={$platform === 'macos'}>
|
||||
{#if $custom_theme_audio_playing || ($active_custom_theme && $active_custom_theme.name !== 'Default')}
|
||||
<div class="relative flex items-center justify-center group">
|
||||
<div
|
||||
class="relative flex items-center justify-center group"
|
||||
role="group"
|
||||
aria-label="Theme volume"
|
||||
bind:this={volumeControl}
|
||||
onpointerenter={openVolume}
|
||||
onpointerleave={closeVolume}
|
||||
>
|
||||
<div class="relative flex items-center justify-center p-1 cursor-pointer">
|
||||
<div
|
||||
class="absolute inset-0 bg-background rounded
|
||||
@@ -51,8 +88,9 @@
|
||||
|
||||
<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
|
||||
{volumeOpen
|
||||
? 'opacity-100 visible pointer-events-auto translate-y-0'
|
||||
: 'opacity-0 invisible pointer-events-none -translate-y-2'}
|
||||
{$reduceAnimations ? '' : 'transition-all duration-200 ease-out'}
|
||||
z-50"
|
||||
>
|
||||
@@ -68,6 +106,7 @@
|
||||
max="1"
|
||||
step="0.01"
|
||||
bind:value={$custom_theme_volume}
|
||||
onpointerdown={startVolumeDrag}
|
||||
oninput={() => {
|
||||
config.value<number>('volume').set($custom_theme_volume);
|
||||
}}
|
||||
|
||||
+35
-1
@@ -7,6 +7,7 @@ import { convertFileSrc, invoke } from '@tauri-apps/api/core';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
import * as path from '@tauri-apps/api/path';
|
||||
import * as fs from '@tauri-apps/plugin-fs';
|
||||
import { tick } from 'svelte';
|
||||
|
||||
export type Theme = {
|
||||
folder_name: string;
|
||||
@@ -486,7 +487,18 @@ export const downloadTheme = async (
|
||||
return { success: true };
|
||||
};
|
||||
|
||||
export const loadTheme = async (theme: Theme, themeContainer: HTMLElement, volume = 0.15) => {
|
||||
type LoadThemeOptions = {
|
||||
/** Crossfade the rendered page when switching themes. */
|
||||
animate?: boolean;
|
||||
};
|
||||
|
||||
export const loadTheme = async (
|
||||
theme: Theme,
|
||||
themeContainer: HTMLElement,
|
||||
volume = 0.15,
|
||||
options: LoadThemeOptions = {}
|
||||
) => {
|
||||
const applyTheme = async () => {
|
||||
// Theme media is buffered as blob URLs; the previous theme's URLs are no
|
||||
// longer needed once it is replaced.
|
||||
clearThemeMediaCache();
|
||||
@@ -514,6 +526,28 @@ export const loadTheme = async (theme: Theme, themeContainer: HTMLElement, volum
|
||||
});
|
||||
|
||||
void invoke('unlock_media_autoplay').catch(() => {});
|
||||
};
|
||||
|
||||
const canAnimate =
|
||||
options.animate &&
|
||||
typeof document !== 'undefined' &&
|
||||
typeof document.startViewTransition === 'function';
|
||||
|
||||
if (!canAnimate) {
|
||||
await applyTheme();
|
||||
return;
|
||||
}
|
||||
|
||||
const transition = document.startViewTransition(async () => {
|
||||
await applyTheme();
|
||||
// Let Svelte paint the new theme before the transition takes its snapshot.
|
||||
await tick();
|
||||
});
|
||||
|
||||
// Theme activation must not wait for, or fail because of, the visual
|
||||
// transition. `finished` can reject when a transition is skipped.
|
||||
void transition.finished.catch(() => {});
|
||||
await transition.updateCallbackDone;
|
||||
};
|
||||
|
||||
export const deleteTheme = async (themeToUninstall: Theme) => {
|
||||
|
||||
@@ -583,6 +583,7 @@
|
||||
</AlertDialog.Root>
|
||||
|
||||
<main
|
||||
class="relative h-screen w-full overflow-hidden"
|
||||
use:appActivity
|
||||
use:themeMediaSuspension
|
||||
use:themeParallax={{
|
||||
|
||||
@@ -526,7 +526,9 @@
|
||||
return;
|
||||
}
|
||||
|
||||
loadTheme(theme, $custom_theme_container, $custom_theme_volume);
|
||||
await loadTheme(theme, $custom_theme_container, $custom_theme_volume, {
|
||||
animate: !$reduceAnimations,
|
||||
});
|
||||
await config.value<string>('theme').set(theme.name);
|
||||
};
|
||||
|
||||
@@ -4511,9 +4513,6 @@
|
||||
<div
|
||||
class="absolute top-1.5 left-1.5 flex max-w-[calc(100%-4.5rem)] flex-wrap items-center gap-1"
|
||||
>
|
||||
{#if isActive}
|
||||
<Badge class="text-[9px]">In use</Badge>
|
||||
{/if}
|
||||
{#if theme.name !== 'Default' && theme.folder_name !== 'default' && theme.apiVersion}
|
||||
<Badge
|
||||
variant="secondary"
|
||||
|
||||
Reference in new issue
Block a user