Merge pull request #423 from afonsojramos/rework-titlebar-drag-region

refactor: rework window drag regions and text selection model
This commit is contained in:
Victor RH authored and GitHub committed 2026-05-13 14:41:06 -06:00
commit 48b83d1403
54 files changed
+135 -150

No files matched your search

+22
View File
@@ -1547,6 +1547,28 @@ body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow: hidden;
/* App-wide non-selection. Music app text (titles, artists, headers) is
not meant to be selected — and selectable text breaks window drag
regions, since native text selection preempts the drag gesture.
Exceptions below opt specific elements back into selectability. */
user-select: none;
-webkit-user-select: none;
}
/* Text-input controls always get native selection back. */
input,
textarea,
[contenteditable]:not([contenteditable='false']) {
user-select: text;
-webkit-user-select: text;
}
/* Escape hatch: add class="selectable" to any element whose text users
should be able to copy (error messages, tokens, release notes, etc.). */
.selectable,
.selectable * {
user-select: text;
-webkit-user-select: text;
}
/* Scrollbar Styling */
+1 -1
View File
@@ -49,7 +49,7 @@
<!-- svelte-ignore a11y_click_events_have_key_events -->
<div class="modal-backdrop" onclick={onClose} role="presentation">
<!-- svelte-ignore a11y_no_static_element_interactions, a11y_click_events_have_key_events -->
<div class="modal" onclick={(e) => e.stopPropagation()}>
<div class="modal selectable" onclick={(e) => e.stopPropagation()}>
<!-- Header -->
<div class="modal-header">
<div class="app-branding">
+1 -1
View File
@@ -173,7 +173,7 @@
<!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions -->
<div class="modal-overlay" onclick={onClose} role="presentation">
<!-- svelte-ignore a11y_click_events_have_key_events -->
<div class="credits-modal" role="dialog" aria-modal="true" tabindex="-1" onclick={(e) => e.stopPropagation()}>
<div class="credits-modal selectable" role="dialog" aria-modal="true" tabindex="-1" onclick={(e) => e.stopPropagation()}>
{#if loading}
<div class="loading-state">
<LoaderCircle size={32} class="spinner" />
+1 -1
View File
@@ -305,7 +305,7 @@
</div>
<!-- Content -->
<div class="booklet-content" bind:this={containerEl}>
<div class="booklet-content selectable" bind:this={containerEl}>
{#if isLoading}
<div class="booklet-loading">
<div class="spinner"></div>
@@ -74,7 +74,6 @@
align-items: center;
gap: 8px;
cursor: pointer;
user-select: none;
color: var(--text-secondary, var(--text-muted));
font-size: 13px;
}
-1
View File
@@ -490,7 +490,6 @@
letter-spacing: 0.5px;
color: var(--text-muted);
pointer-events: none;
user-select: none;
}
.option {
+1 -1
View File
@@ -461,7 +461,7 @@
}
</script>
<div class="diagnostics-panel">
<div class="diagnostics-panel selectable">
<button class="section-toggle panel-toggle" onclick={togglePanel}>
{#if panelOpen}<ChevronDown size={14} />{:else}<ChevronRight size={14} />{/if}
<h4 class="subsection-title" style="margin:0">{$t('settings.developer.diagnostics.title')}</h4>
@@ -243,7 +243,6 @@
.playlist-card {
width: 140px;
cursor: pointer;
user-select: none;
}
.artwork-container {
@@ -189,12 +189,6 @@
overflow-x: auto;
overflow-y: hidden;
cursor: grab;
/* Never select text inside carousels: drag-to-scroll would otherwise
highlight titles/artists in the first few pixels before the drag
threshold kicks in. Text is still selectable in the dedicated
album/artist/playlist views. */
user-select: none;
-webkit-user-select: none;
}
.scroll-container.dragging {
@@ -90,7 +90,7 @@
>
<!-- svelte-ignore a11y_click_events_have_key_events -->
<div
class="modal"
class="modal selectable"
role="dialog"
aria-modal="true"
aria-labelledby="shortcuts-modal-title"
@@ -326,7 +326,6 @@
min-height: 22px;
cursor: pointer;
border-radius: 3px;
user-select: none;
color: var(--text-primary);
transition: background 100ms ease;
/* Extend to natural content width when names are long; fill the
-1
View File
@@ -294,7 +294,6 @@
cursor: pointer;
font-weight: 500;
color: var(--text-primary);
user-select: none;
}
.error-list {
+1 -1
View File
@@ -71,7 +71,7 @@
aria-labelledby="musician-modal-title"
tabindex="-1"
>
<div class="modal">
<div class="modal selectable">
<!-- Header -->
<div class="modal-header">
<div class="musician-info">
+1 -2
View File
@@ -23,7 +23,7 @@
const TERMS_URL = 'https://www.qobuz.com/us-en/legal/terms';
</script>
<div class={`qobuz-legal ${className}`}>
<div class={`qobuz-legal selectable ${className}`}>
{#if showBrandingLine}
<p class="branding-line">
{$t('legal.brandingLine')}
@@ -74,7 +74,6 @@
font-size: 12px;
color: var(--text-muted);
line-height: 1.4;
user-select: none;
}
.tos-line input {
@@ -242,7 +242,6 @@
.playlist-card {
width: 162px;
cursor: pointer;
user-select: none;
}
.artwork-container {
+15 -5
View File
@@ -1650,9 +1650,14 @@
<svelte:window onclick={handleGlobalClick} />
<aside class="sidebar" class:collapsed={!isExpanded} class:no-titlebar={!showTitleBar}>
<aside
class="sidebar"
class:collapsed={!isExpanded}
class:no-titlebar={!showTitleBar}
data-tauri-drag-region={!showTitleBar ? '' : undefined}
>
<!-- Scrollable Content Area -->
<div class="content">
<div class="content" data-tauri-drag-region="false">
<!-- Search Bar (hidden when search is in titlebar) -->
{#if !searchInTitlebar}
<div
@@ -2256,6 +2261,7 @@
class="toggle-btn"
onclick={onToggle}
title={isExpanded ? $t('actions.collapse') : $t('actions.expand')}
data-tauri-drag-region="false"
>
{#if isExpanded}
<ChevronLeft size={16} />
@@ -2265,7 +2271,7 @@
</button>
<!-- Fixed User Profile at Bottom -->
<div class="user-section" class:collapsed={!isExpanded}>
<div class="user-section" class:collapsed={!isExpanded} data-tauri-drag-region="false">
<UserCard
username={userName}
{subscription}
@@ -2515,7 +2521,12 @@
height: calc(100vh - 104px); /* Only 104px NowPlayingBar, no title bar */
}
/* macOS: pad top of sidebar to clear native traffic light buttons */
/* macOS: pad top of sidebar to clear native traffic light buttons.
This 32px band is also the only visible drag surface: the <aside>
carries `data-tauri-drag-region` (set in the template when there's
no custom titlebar), while each direct child carries
`data-tauri-drag-region="false"` so clicks inside .content / the
collapse toggle / the user section never start a window drag. */
:global(html.macos) .sidebar.no-titlebar {
padding-top: 32px;
}
@@ -3487,7 +3498,6 @@
color: var(--text-secondary);
cursor: pointer;
transition: color 150ms ease, background-color 150ms ease;
user-select: none;
}
.my-qbz-parent:hover {
@@ -305,7 +305,6 @@
color: var(--color-success, #22c55e);
font-variant-numeric: tabular-nums;
line-height: 1;
user-select: none;
pointer-events: none;
}
-5
View File
@@ -288,10 +288,6 @@
display: flex;
align-items: center;
padding: 2px 0 0 0;
user-select: none;
-webkit-user-select: none;
-webkit-app-region: drag;
app-region: drag;
}
/* 3-zone layout: left and right zones are equal width, center is fixed */
@@ -321,7 +317,6 @@
.drag-region {
flex: 1;
height: 100%;
cursor: default;
}
/* Search bar in titlebar */
+1 -1
View File
@@ -169,7 +169,7 @@
<!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions -->
<div class="modal-overlay" onclick={onClose} role="presentation">
<!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions -->
<div class="track-modal" onclick={(e) => e.stopPropagation()} role="dialog" aria-modal="true" tabindex="-1">
<div class="track-modal selectable" onclick={(e) => e.stopPropagation()} role="dialog" aria-modal="true" tabindex="-1">
{#if loading}
<div class="loading-state">
<LoaderCircle size={32} class="spinner" />
@@ -137,7 +137,7 @@
);
</script>
<div class="track-info-panel" class:centered-layout={centeredLayout}>
<div class="track-info-panel selectable" class:centered-layout={centeredLayout}>
{#if loading}
<div class="loading-state">
<LoaderCircle size={28} class="spinner" />
@@ -36,7 +36,7 @@
</script>
<aside class="lyrics-sidebar">
<div class="header">
<div class="header" data-tauri-drag-region="deep">
<div class="header-icon">
<MicVocal size={18} />
</div>
@@ -65,7 +65,7 @@
/>
</div>
<div class="panel">
<div class="panel selectable">
{#if isLoading}
<div class="state">
<div class="loading-spinner"></div>
@@ -664,7 +664,7 @@
</div>
<!-- Album Metadata -->
<div class="metadata" class:no-description={!album.description}>
<div class="metadata selectable" class:no-description={!album.description}>
<h1 class="album-title">{album.title}</h1>
<div class="artist-line">
{#if album.parentalWarning}
@@ -819,8 +819,8 @@
<!-- Track List -->
<div class="track-list">
<!-- Table Header -->
<div class="tracklist-toolbar">
<div class="tracklist-toolbar-left">
<div class="tracklist-toolbar" data-tauri-drag-region="deep">
<div class="tracklist-toolbar-left" data-tauri-drag-region="false">
<QualityBadgeStatic
bare
quality={album.quality}
@@ -828,7 +828,7 @@
samplingRate={album.samplingRate}
/>
</div>
<div class="tracklist-toolbar-search">
<div class="tracklist-toolbar-search" data-tauri-drag-region="false">
<Search size={14} />
<input
type="text"
@@ -1789,7 +1789,7 @@
});
</script>
<div class="artist-detail" class:has-art-bg={!!headerColor} style={headerStyle} bind:this={artistDetailEl} onscroll={(e) => saveScrollPosition('artist', (e.target as HTMLElement).scrollTop, artist.id)}>
<div class="artist-detail" class:has-art-bg={!!headerColor} style={headerStyle} bind:this={artistDetailEl} onscroll={(e) => saveScrollPosition('artist', (e.target as HTMLElement).scrollTop, artist.id)} data-tauri-drag-region>
<!-- Back Navigation -->
<button class="back-btn" onclick={onBack}>
<ArrowLeft size={16} />
@@ -1826,7 +1826,7 @@
</div>
<!-- Artist Info -->
<div class="artist-info">
<div class="artist-info selectable">
<h1 class="artist-name">{artist.name}</h1>
<!-- Biography -->
@@ -2145,7 +2145,7 @@
<!-- Top Tracks Section -->
{#if topTracks.length > 0 || tracksLoading}
<div class="top-tracks-section section-anchor" bind:this={topTracksSection}>
<div class="section-header">
<div class="section-header" data-tauri-drag-region="deep">
<div class="section-header-left">
<h2 class="section-title">{$t('artist.popularTracks')}</h2>
</div>
@@ -2391,7 +2391,7 @@
<!-- Discography Section -->
<div class="discography section-anchor" bind:this={discographySection}>
<div class="section-header">
<div class="section-header" data-tauri-drag-region="deep">
<div class="section-header-left">
<h2 class="section-title">{$t('artist.discography')}</h2>
{#if artist.albums.length > 0}
@@ -2493,7 +2493,7 @@
<div class="divider"></div>
<div class="discography section-anchor" bind:this={epsSinglesSection}>
<div class="section-header">
<div class="section-header" data-tauri-drag-region="deep">
<div class="section-header-left">
<h2 class="section-title">{$t('artist.epsSingles')}</h2>
<span class="section-count">{artist.epsSingles.length}</span>
@@ -2557,7 +2557,7 @@
<div class="divider"></div>
<div class="discography section-anchor" bind:this={liveAlbumsSection}>
<div class="section-header">
<div class="section-header" data-tauri-drag-region="deep">
<div class="section-header-left">
<h2 class="section-title">{$t('artist.liveAlbums')}</h2>
<span class="section-count">{artist.liveAlbums.length}</span>
@@ -2621,7 +2621,7 @@
<div class="divider"></div>
<div class="discography section-anchor" bind:this={compilationsSection}>
<div class="section-header">
<div class="section-header" data-tauri-drag-region="deep">
<div class="section-header-left">
<h2 class="section-title">{$t('artist.compilations')}</h2>
<span class="section-count">{artist.compilations.length}</span>
@@ -2678,7 +2678,7 @@
<div class="divider"></div>
<div class="discography section-anchor" bind:this={othersSection}>
<div class="section-header">
<div class="section-header" data-tauri-drag-region="deep">
<div class="section-header-left">
<h2 class="section-title">{$t('artist.others')}</h2>
<span class="section-count">{artist.others.length}</span>
@@ -2742,7 +2742,7 @@
<div class="divider"></div>
<div class="playlists-section section-anchor" bind:this={playlistsSection}>
<div class="section-header">
<div class="section-header" data-tauri-drag-region="deep">
<div class="section-header-left">
<h2 class="section-title">{$t('artist.playlists')}</h2>
<span class="section-count">{artist.playlists.length}</span>
@@ -2789,7 +2789,7 @@
<div class="divider"></div>
<div class="discography section-anchor" bind:this={tributesSection}>
<div class="section-header">
<div class="section-header" data-tauri-drag-region="deep">
<div class="section-header-left">
<h2 class="section-title">{$t('artist.tributes')}</h2>
<span class="section-count">{artist.tributes.length}</span>
@@ -422,7 +422,7 @@
<div class="scene-view">
{#if !loading}
<!-- Back button (always visible) -->
<div class="top-bar">
<div class="top-bar" data-tauri-drag-region="deep">
<button class="back-btn" onclick={onBack}>
<ArrowLeft size={16} />
<span>{$t('actions.back')}</span>
@@ -431,7 +431,7 @@
<!-- Compact sticky header (appears when hero scrolls out) -->
{#if heroScrolledPast}
<div class="compact-header">
<div class="compact-header" data-tauri-drag-region="deep">
{#if flagUrl}
<img src={flagUrl} alt="" class="compact-flag" />
{/if}
@@ -163,8 +163,8 @@
</script>
<div class="award-albums-view">
<div class="top-bar">
<div class="top-bar-left">
<div class="top-bar" data-tauri-drag-region="deep">
<div class="top-bar-left" data-tauri-drag-region="false">
<button class="back-btn" onclick={onBack} title={$t('actions.back')}>
<ChevronLeft size={20} />
</button>
@@ -173,7 +173,7 @@
<h1 class="page-title">{$t('award.section.releases')}</h1>
</div>
</div>
<div class="top-bar-right">
<div class="top-bar-right" data-tauri-drag-region="false">
<div class="search-wrapper">
<Search size={16} />
<input
+2 -2
View File
@@ -227,13 +227,13 @@
const heroImage = $derived(page?.image || page?.magazine?.image || '');
</script>
<div class="award-detail-view">
<div class="award-detail-view" data-tauri-drag-region>
<button class="back-btn" onclick={onBack}>
<ArrowLeft size={16} />
<span>{$t('actions.back')}</span>
</button>
<header class="award-header">
<header class="award-header" data-tauri-drag-region="deep">
<div class="award-image-wrapper">
{#if heroImage && !heroImageFailed}
<img
@@ -105,7 +105,7 @@
<ViewTransition duration={200} distance={12} direction="down">
<div class="blacklist-manager">
<div class="top-bar">
<div class="top-bar" data-tauri-drag-region="deep">
<button class="back-btn" onclick={onBack}>
<ArrowLeft size={16} />
<span>{$t('actions.back')}</span>
@@ -181,9 +181,9 @@
<span>{$t('actions.back')}</span>
</button>
{/if}
<header class="view-header">
<header class="view-header" data-tauri-drag-region="deep">
<h1>{$t('collections.nav')}</h1>
<div class="header-actions">
<div class="header-actions" data-tauri-drag-region="false">
<button
type="button"
class="primary-cta"
@@ -567,7 +567,7 @@
</script>
<div class="builder-view">
<div class="builder-scroll">
<div class="builder-scroll" data-tauri-drag-region>
{#if onBack}
<button class="back-btn" onclick={() => onBack?.()}>
<ArrowLeft size={16} />
@@ -575,7 +575,7 @@
</button>
{/if}
<header class="builder-header">
<header class="builder-header" data-tauri-drag-region="deep">
{#if artistAvatarUrl}
<img class="avatar" src={artistAvatarUrl} alt="" />
{:else}
@@ -1157,7 +1157,6 @@
color: var(--text-muted);
font-size: 12px;
line-height: 1;
user-select: none;
flex-shrink: 0;
}
@@ -234,14 +234,14 @@
<div class="discover-browse">
<!-- Top bar -->
<div class="top-bar">
<div class="top-bar-left">
<div class="top-bar" data-tauri-drag-region="deep">
<div class="top-bar-left" data-tauri-drag-region="false">
<button class="back-btn" onclick={onBack}>
<ChevronLeft size={20} />
</button>
<h1 class="page-title">{$t(titleKey)}</h1>
</div>
<div class="top-bar-right">
<div class="top-bar-right" data-tauri-drag-region="false">
<div class="search-wrapper">
<Search size={16} />
<input
@@ -163,14 +163,14 @@
<div class="discover-playlists">
<!-- Top bar -->
<div class="top-bar">
<div class="top-bar-left">
<div class="top-bar" data-tauri-drag-region="deep">
<div class="top-bar-left" data-tauri-drag-region="false">
<button class="back-btn" onclick={onBack}>
<ChevronLeft size={20} />
</button>
<h1 class="page-title">{$t('discover.qobuzPlaylists')}</h1>
</div>
<div class="top-bar-right">
<div class="top-bar-right" data-tauri-drag-region="false">
<div class="search-wrapper">
<Search size={16} />
<input
@@ -514,7 +514,7 @@
</button>
</div>
<div class="playlist-header">
<div class="playlist-header" data-tauri-drag-region="deep">
<div class="artwork-container">
<div class="artwork artwork-daily"></div>
</div>
@@ -594,7 +594,7 @@
<div class="empty">{$t('qobuzMixes.result.empty')}</div>
{:else if result}
<div class="track-list">
<div class="track-list-header">
<div class="track-list-header" data-tauri-drag-region="deep">
{#if multiSelectMode}
<div class="col-select-all">
<input
+2 -2
View File
@@ -442,7 +442,7 @@
</button>
</div>
<div class="playlist-header">
<div class="playlist-header" data-tauri-drag-region="deep">
<div class="artwork-container">
<div class="artwork artwork-favq"></div>
</div>
@@ -525,7 +525,7 @@
<div class="empty">{$t('qobuzMixes.result.empty')}</div>
{:else if tracks.length > 0}
<div class="track-list">
<div class="track-list-header">
<div class="track-list-header" data-tauri-drag-region="deep">
{#if multiSelectMode}
<div class="col-select-all">
<input
@@ -1439,7 +1439,7 @@
<ViewTransition duration={200} distance={12} direction="down">
<div class="favorites-view" class:no-outer-scroll={(activeTab === 'tracks' && !loading && filteredTracks.length > 0) || (activeTab === 'albums' && !loading && filteredAlbums.length > 0) || (activeTab === 'artists' && !loading && filteredArtists.length > 0)} bind:this={scrollContainer} onscroll={handleFavoritesScroll}>
<div class="top-bar">
<div class="top-bar" data-tauri-drag-region="deep">
{#if onBack}
<button class="back-btn" onclick={onBack}>
<ArrowLeft size={16} />
@@ -1451,10 +1451,10 @@
</button>
</div>
<!-- Header -->
<div class="header">
<div class="header" data-tauri-drag-region="deep">
<h1>{$t('favorites.title')}</h1>
{#if activeTab === 'tracks' && !loading && filteredTracks.length > 0}
<div class="header-actions">
<div class="header-actions" data-tauri-drag-region="false">
<button class="action-btn-circle" onclick={handlePlayAllTracks} title={$t('actions.playAll')}>
<Play size={18} fill="currentColor" color="currentColor" />
</button>
@@ -1483,20 +1483,20 @@
</div>
{/if}
{#if activeTab === 'albums' && !loading && filteredAlbums.length > 0}
<div class="header-actions">
<div class="header-actions" data-tauri-drag-region="false">
<button class="action-btn-circle" onclick={handleRandomAlbum} title={$t('favorites.randomAlbum')}>
<Shuffle size={18} />
</button>
</div>
{/if}
{#if activeTab === 'artists' && !loading && filteredArtists.length > 0}
<div class="header-actions">
<div class="header-actions" data-tauri-drag-region="false">
<button class="action-btn-circle" onclick={handleRandomArtist} title={$t('favorites.randomArtist')}>
<Shuffle size={18} />
</button>
</div>
{/if}
<div class="header-search">
<div class="header-search" data-tauri-drag-region="false">
{#if !searchExpanded}
<button class="search-icon-btn" onclick={() => searchExpanded = true} title={$t('nav.search')}>
<Search size={16} />
@@ -1522,8 +1522,8 @@
</div>
<!-- Navigation Bar (Artist-style) -->
<div class="favorites-nav">
<div class="nav-left">
<div class="favorites-nav" data-tauri-drag-region="deep">
<div class="nav-left" data-tauri-drag-region="false">
{#each favoritesPreferences.tab_order as tab}
{@const Icon = getTabIcon(tab as TabType)}
<button
@@ -1536,7 +1536,7 @@
</button>
{/each}
</div>
<div class="nav-right">
<div class="nav-right" data-tauri-drag-region="false">
<span class="results-count">
{#if activeTab === 'tracks'}
{filteredTracks.length}{trackSearch ? ` / ${favoriteTracks.length}` : ''} {$t('favorites.tracks').toLowerCase()}
@@ -345,7 +345,7 @@
<div class="label-view" onscroll={handleScroll}>
<!-- Back button -->
<div class="top-bar">
<div class="top-bar" data-tauri-drag-region="deep">
<button class="back-btn" onclick={onBack}>
<ArrowLeft size={16} />
<span>{$t('actions.back')}</span>
@@ -353,7 +353,7 @@
</div>
<!-- Header -->
<header class="label-header">
<header class="label-header" data-tauri-drag-region="deep">
<div class="label-image-wrapper">
{#if getLabelImage()}
<img src={getLabelImage()} alt={label?.name || labelName} class="label-image" loading="lazy" decoding="async" />
+2 -2
View File
@@ -862,7 +862,7 @@
});
</script>
<div class="label-detail-view" bind:this={labelDetailEl}>
<div class="label-detail-view" bind:this={labelDetailEl} data-tauri-drag-region>
{#if loading}
<div class="loading-state">
<div class="spinner"></div>
@@ -882,7 +882,7 @@
</button>
<!-- Header -->
<header class="label-header section-anchor" bind:this={headerSection}>
<header class="label-header section-anchor" bind:this={headerSection} data-tauri-drag-region="deep">
<div class="label-image-wrapper">
{#if getLabelImage(pageData)}
<img
@@ -5740,7 +5740,7 @@
</div>
{:else}
<!-- Main Library View -->
<div class="header">
<div class="header" data-tauri-drag-region="deep">
<div class="header-content">
<h1>{$t('library.title')}</h1>
{#if stats}
@@ -5753,7 +5753,7 @@
<p class="subtitle">{$t('library.yourCollection')}</p>
{/if}
</div>
<div class="header-actions">
<div class="header-actions" data-tauri-drag-region="false">
{#if hasPlexConfig()}
<button
class="icon-btn plex-sync-btn"
@@ -8188,7 +8188,6 @@
font-size: 13px;
color: var(--text-secondary);
cursor: pointer;
user-select: none;
}
.select-all-checkbox input[type='checkbox'] {
@@ -9288,9 +9287,6 @@
display: flex;
align-items: center;
justify-content: center;
/* Block selection bleed on the handle itself; the global override
on <body> during drag handles the rest of the page. */
user-select: none;
}
.tree-sidebar-resize-handle:hover,
@@ -1412,7 +1412,7 @@
</div>
{:else}
<!-- Header -->
<header class="detail-header">
<header class="detail-header" data-tauri-drag-region="deep">
{#if onBack}
<button class="back-btn" onclick={() => onBack()}>
<ArrowLeft size={16} />
+1 -1
View File
@@ -153,7 +153,7 @@
<span>{$t('actions.back')}</span>
</button>
{/if}
<header class="view-header">
<header class="view-header" data-tauri-drag-region="deep">
<h1>{$t('mixtapes.nav')}</h1>
<button
type="button"
@@ -91,7 +91,7 @@
<div class="musician-page">
<!-- Header -->
<header class="page-header">
<header class="page-header" data-tauri-drag-region="deep">
<button class="back-btn" onclick={onBack} title={ $t('actions.back') }>
<ArrowLeft size={20} />
</button>
@@ -212,7 +212,7 @@
</script>
<div class="offline-cache-manager">
<header class="ocm-header">
<header class="ocm-header" data-tauri-drag-region="deep">
<button type="button" class="back-btn" onclick={onBack} aria-label={$t('actions.back')}>
<ArrowLeft size={18} />
</button>
@@ -2420,7 +2420,7 @@
{#if playlist.description}
<p class="playlist-description">{@html sanitizeHtml(playlist.description)}</p>
{/if}
<div class="playlist-info">
<div class="playlist-info selectable">
<span class="owner">{playlist.owner.name}</span>
<span class="separator">•</span>
<span>{totalTrackCount} {$t('playlist.tracks')}{#if hasLocalTracks} <span class="local-count">({localTracks.length} local)</span>{/if}</span>
@@ -2518,9 +2518,9 @@
tall as the virtualized content, so the header pins for the whole
scroll range. -->
<div class="track-list" bind:this={trackListEl}>
<div class="tracks-sticky-header">
<div class="tracks-sticky-header" data-tauri-drag-region="deep">
<!-- Track List Controls -->
<div class="track-controls">
<div class="track-controls" data-tauri-drag-region="false">
<!-- Search -->
<div class="search-container">
<Search size={16} class="search-icon" />
@@ -3450,7 +3450,6 @@
.track-row-wrapper.unavailable {
opacity: 0.5;
pointer-events: auto;
user-select: none;
}
.track-row-wrapper.unavailable :global(.track-row) {
@@ -890,14 +890,14 @@
<span>{$t('actions.back')}</span>
</button>
<!-- Header -->
<div class="header">
<div class="header" data-tauri-drag-region="deep">
<h1>{$t('playlistManager.heading')}</h1>
</div>
<!-- Breadcrumb Navigation (when inside a folder, only in folder mode and not tree) -->
{#if folderMode && viewMode !== 'tree' && currentFolderId && currentFolder}
{@const breadcrumbFolder = currentFolder}
<div class="breadcrumb">
<div class="breadcrumb" data-tauri-drag-region="deep">
<button class="breadcrumb-item" onclick={navigateToRoot}>
{$t('playlist.allPlaylists')}
</button>
@@ -218,7 +218,7 @@
</script>
<ViewTransition duration={200} distance={12} direction="up">
<div class="purchase-album-detail">
<div class="purchase-album-detail" data-tauri-drag-region>
<!-- Back Navigation -->
<button class="back-btn" onclick={onBack}>
<ArrowLeft size={16} />
@@ -235,7 +235,7 @@
</div>
{:else if album}
<!-- Album Header -->
<div class="album-header">
<div class="album-header" data-tauri-drag-region="deep">
<div class="artwork" class:unavailable={!album.downloadable}>
<img src={getQobuzImage(album.image)} alt={album.title} />
{#if !album.downloadable}
@@ -502,7 +502,7 @@
<div class="purchases-view">
<!-- Header -->
<div class="header">
<div class="header" data-tauri-drag-region="deep">
<div class="header-icon">
<ShoppingBag size={32} color="var(--accent-primary)" />
</div>
@@ -521,8 +521,8 @@
{/if}
<!-- Sticky Navigation Bar -->
<div class="purchases-nav">
<div class="nav-left">
<div class="purchases-nav" data-tauri-drag-region="deep">
<div class="nav-left" data-tauri-drag-region="false">
<button
class="nav-link"
class:active={activeTab === 'albums'}
@@ -159,7 +159,7 @@
</script>
<div class="release-watch">
<div class="top-bar">
<div class="top-bar" data-tauri-drag-region="deep">
<button class="back-btn" onclick={onBack} title={$t('actions.back')}>
<ChevronLeft size={20} />
</button>
@@ -169,7 +169,7 @@
</div>
</div>
<div class="tabs">
<div class="tabs" data-tauri-drag-region="deep">
{#each tabs as tab}
{@const Icon = tab.icon}
<button
+3 -3
View File
@@ -1136,9 +1136,9 @@
{/if}
<!-- Sticky Header Container -->
<div class="sticky-header" class:scrolled={isScrolled}>
<div class="sticky-header" class:scrolled={isScrolled} data-tauri-drag-region="deep">
{#if !searchInTitlebar}
<div class="search-input-container" class:compact={isScrolled}>
<div class="search-input-container" class:compact={isScrolled} data-tauri-drag-region="false">
<Search size={isScrolled ? 18 : 20} class="search-icon" />
<input
type="text"
@@ -1157,7 +1157,7 @@
{/if}
<!-- Tabs and Filters Row -->
<div class="tabs-row">
<div class="tabs-row" data-tauri-drag-region="false">
<div class="tabs">
<button
class="tab"
+1 -2
View File
@@ -4181,7 +4181,7 @@
{/if}
<!-- Header -->
<div class="header">
<div class="header" data-tauri-drag-region="deep">
{#if onBack}
<button class="back-btn" onclick={onBack}>
<ArrowLeft size={16} />
@@ -7157,7 +7157,6 @@ flatpak override --user --filesystem=/home/USUARIO/Música com.blitzfc.qbz</pre>
color: var(--text-secondary);
font-size: 13px;
list-style: none;
user-select: none;
}
.discord-rpc-override > summary::-webkit-details-marker {
+2 -2
View File
@@ -570,7 +570,7 @@
</button>
</div>
<div class="playlist-header">
<div class="playlist-header" data-tauri-drag-region="deep">
<div class="artwork-container">
<div class="artwork artwork-topq"></div>
</div>
@@ -653,7 +653,7 @@
<div class="empty">{$t('qobuzMixes.result.empty')}</div>
{:else if result}
<div class="track-list">
<div class="track-list-header">
<div class="track-list-header" data-tauri-drag-region="deep">
{#if multiSelectMode}
<div class="col-select-all">
<input
@@ -512,7 +512,7 @@
</button>
</div>
<div class="playlist-header">
<div class="playlist-header" data-tauri-drag-region="deep">
<div class="artwork-container">
<div class="artwork artwork-weekly"></div>
</div>
@@ -589,7 +589,7 @@
<div class="empty">{$t('qobuzMixes.result.empty')}</div>
{:else if result}
<div class="track-list">
<div class="track-list-header">
<div class="track-list-header" data-tauri-drag-region="deep">
{#if multiSelectMode}
<div class="col-select-all">
<input
@@ -251,7 +251,5 @@
.grid {
display: grid;
gap: 8px 16px;
user-select: none;
-webkit-user-select: none;
}
</style>
@@ -249,7 +249,5 @@
.row {
display: flex;
gap: 32px;
user-select: none;
-webkit-user-select: none;
}
</style>
+3 -3
View File
@@ -364,8 +364,8 @@
</script>
<div class="discovery">
<div class="toolbar">
<div class="tabs">
<div class="toolbar" data-tauri-drag-region>
<div class="tabs" data-tauri-drag-region="false">
{#each tabs as tab (tab.id)}
<button
class="tab"
@@ -377,7 +377,7 @@
</button>
{/each}
</div>
<div class="genre-slot">
<div class="genre-slot" data-tauri-drag-region="false">
<GenreFilterButton
onFilterChange={handleGenreFilterChange}
context="home"
+9 -26
View File
@@ -5910,10 +5910,15 @@
class:match-chrome={matchSystemChrome && showTitleBar && windowTransparent}
style="--chrome-radius: {chromeRadiusPx}px;"
>
<!-- macOS: drag region for window movement (overlay title bar has no native drag area) -->
{#if !showTitleBar && platform === 'macos'}
<div class="macos-drag-region" data-tauri-drag-region></div>
{/if}
<!--
macOS overlay title bar: drag handling lives on the existing
.sidebar element. It already has a 32px top padding band (to
clear the traffic lights) — that's the area we reuse as the
window drag surface, by tagging the <aside> as a drag region
and tagging each of its direct children as no-drag. No new
elements added, no spacing added — just attributes.
-->
<!-- Custom Title Bar (CSD) -->
{#if showTitleBar}
<TitleBar
@@ -7382,28 +7387,6 @@
height: calc(100vh - var(--player-bar-height, 104px));
}
/* macOS: pad main content to clear native overlay title bar */
:global(html.macos) .main-content {
padding-top: 16px;
height: calc(100vh - 104px - 16px);
}
/* macOS: home view handles its own spacing */
:global(html.macos) .main-content :global(.home-view) {
margin-top: -16px;
}
/* macOS: invisible drag region for window movement (overlay title bar) */
:global(html.macos) .macos-drag-region {
height: 28px;
width: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 9999;
-webkit-app-region: drag;
}
.view-error {
display: flex;
flex-direction: column;