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