Crossfade user-selected themes, keep the theme volume popover open while dragging its slider, and clip the fixed app shell so oversized theme layers cannot create page scrollbars on Windows. Remove the redundant in-use badge from the active theme card.
215 lines
6.1 KiB
Svelte
215 lines
6.1 KiB
Svelte
<script lang="ts">
|
|
import ThemeIcon from '$lib/components/theme/ThemeIcon.svelte';
|
|
import { config } from '$lib/config';
|
|
import {
|
|
active_custom_theme,
|
|
custom_theme_audio_playing,
|
|
custom_theme_volume,
|
|
platform,
|
|
} from '$lib/global';
|
|
import { DEFAULT_THEME_ICON_SET } from '$lib/theme/themeIcons';
|
|
import { reduceAnimations } from '$lib/userSettings';
|
|
import Minimize from '@lucide/svelte/icons/minus';
|
|
import Close from '@lucide/svelte/icons/x';
|
|
import { getCurrentWindow } from '@tauri-apps/api/window';
|
|
import { onMount } from 'svelte';
|
|
|
|
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();
|
|
|
|
document
|
|
.getElementById('titlebar-minimize')
|
|
?.addEventListener('click', () => appWindow.minimize());
|
|
document.getElementById('titlebar-close')?.addEventListener('click', () => appWindow.close());
|
|
});
|
|
</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"
|
|
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
|
|
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}
|
|
<ThemeIcon name="volume" set={iconSet} class="z-10 pointer-events-none" size={14} />
|
|
{:else if $custom_theme_volume >= 0.15}
|
|
<ThemeIcon name="volume-low" set={iconSet} class="z-10 pointer-events-none" size={14} />
|
|
{:else if $custom_theme_volume > 0}
|
|
<ThemeIcon name="volume-low" set={iconSet} class="z-10 pointer-events-none" size={14} />
|
|
{:else}
|
|
<ThemeIcon name="volume-off" set={iconSet} class="z-10 pointer-events-none" size={14} />
|
|
{/if}
|
|
</div>
|
|
|
|
<div
|
|
class="absolute top-full right-0 translate-x-[7px]
|
|
{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"
|
|
>
|
|
<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}
|
|
onpointerdown={startVolumeDrag}
|
|
oninput={() => {
|
|
config.value<number>('volume').set($custom_theme_volume);
|
|
}}
|
|
class="volume-slider"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
<div class="relative flex items-center justify-center p-1 group" id="titlebar-minimize">
|
|
<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'}
|
|
origin-center"
|
|
></div>
|
|
|
|
<Minimize class="z-10 pointer-events-none" size={14} />
|
|
</div>
|
|
<div class="relative flex items-center justify-center p-1 group" id="titlebar-close">
|
|
<div
|
|
class="absolute inset-0 bg-red-500 rounded
|
|
scale-80 opacity-0
|
|
group-hover:opacity-100
|
|
group-hover:scale-100
|
|
{$reduceAnimations ? '' : 'transition-all duration-200 ease-out'}
|
|
origin-center"
|
|
></div>
|
|
|
|
<Close class="z-10 pointer-events-none" size={14} />
|
|
</div>
|
|
</div>
|
|
|
|
<style lang="scss">
|
|
.titlebar {
|
|
height: 35px;
|
|
user-select: none;
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 5px;
|
|
align-items: center;
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
margin-bottom: 10px;
|
|
padding-right: 5px;
|
|
pointer-events: auto;
|
|
}
|
|
|
|
/* Keep the window controls clear of the rounded top-right corner. */
|
|
.titlebar.macos-window {
|
|
padding-right: 12px;
|
|
}
|
|
|
|
.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>
|