Merge pull request #265 from afonsojramos/gate-flatpak-snap-macos

Reviewed: clean platform detection refactor, no audio code touched, all gates correct. Lockfiles will be regenerated post-merge.
This commit is contained in:
Victor RH authored and GitHub committed 2026-04-05 21:18:31 -06:00
commit 5a4f014b79
10 files changed
+64 -31

No files matched your search

+1
View File
@@ -31,6 +31,7 @@
"@tauri-apps/plugin-clipboard-manager": "^2",
"@tauri-apps/plugin-dialog": "^2.5.0",
"@tauri-apps/plugin-opener": "^2",
"@tauri-apps/plugin-os": "^2",
"lucide-svelte": "^1.0.1",
"svelte-i18n": "^4.0.0"
},
+1
View File
@@ -34,6 +34,7 @@ qconnect-transport-ws = { path = "../crates/qconnect-transport-ws" }
# Tauri
tauri = { version = "2", features = ["protocol-asset", "tray-icon", "macos-private-api"] }
tauri-plugin-opener = "2"
tauri-plugin-os = "2"
tauri-plugin-single-instance = { version = "2", features = ["deep-link"] }
tauri-plugin-deep-link = "2"
+1
View File
@@ -33,6 +33,7 @@
"core:webview:allow-webview-close",
"core:webview:allow-set-webview-zoom",
"opener:default",
"os:default",
"dialog:default",
"clipboard-manager:default",
"clipboard-manager:allow-write-text"
+1
View File
@@ -745,6 +745,7 @@ pub fn run() {
}))
.plugin(tauri_plugin_deep_link::init())
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_clipboard_manager::init())
.manage(AppState::with_device_and_settings(
+3 -4
View File
@@ -6,6 +6,7 @@
import { getName, getVersion } from '@tauri-apps/api/app';
import { onMount } from 'svelte';
import QobuzLegalNotice from '$lib/components/QobuzLegalNotice.svelte';
import { platform } from '$lib/utils/platform';
interface Props {
isOpen: boolean;
@@ -37,9 +38,7 @@
}
});
// Detect platform for Easter eggs
const isMac = typeof navigator !== 'undefined' && /Mac/.test(navigator.platform);
const platformLabel = isMac ? 'macOS (Tauri 2.0)' : 'Linux (Tauri 2.0)';
const platformLabel = platform === 'macos' ? 'macOS (Tauri 2.0)' : platform === 'windows' ? 'Windows (Tauri 2.0)' : 'Linux (Tauri 2.0)';
function handleOpenUrl(url: string) {
openUrl(url).catch(err => console.error('Failed to open URL:', err));
@@ -193,7 +192,7 @@
<img src="/mexico-flag.svg" alt="México" class="inline-icon flag" />
</p>
<p class="signature-detail">
{$t('about.signatureDetail')} <img src="/Tux.svg" alt="Tux" class="inline-icon tux" class:mac-tux={isMac} />
{$t('about.signatureDetail')} <img src="/Tux.svg" alt="Tux" class="inline-icon tux" class:mac-tux={platform === 'macos'} />
</p>
</div>
</div>
+23 -12
View File
@@ -13,6 +13,7 @@
import RemoteControlSetupGuide from '../RemoteControlSetupGuide.svelte';
import LogsModal from '../LogsModal.svelte';
import DiagnosticsPanel from '../DiagnosticsPanel.svelte';
import { platform } from '$lib/utils/platform';
import VolumeSlider from '../VolumeSlider.svelte';
import UpdateCheckResultModal from '../updates/UpdateCheckResultModal.svelte';
import WhatsNewModal from '../updates/WhatsNewModal.svelte';
@@ -1470,9 +1471,11 @@
updatesCurrentVersion = getUpdatesCurrentVersion();
});
// Detect sandbox environments
loadFlatpakStatus();
loadSnapStatus();
// Detect sandbox environments (Linux-only)
if (platform === 'linux') {
loadFlatpakStatus();
loadSnapStatus();
}
// Check for legacy cached files
checkLegacyCachedFiles();
@@ -3882,6 +3885,7 @@
/>
</div>
{/if}
{#if platform === 'linux'}
<div class="setting-row">
<div class="setting-info">
<span class="setting-label">{$t('settings.audio.audioBackend')}</span>
@@ -3915,6 +3919,7 @@
{/if}
</div>
</div>
{/if}
<div class="setting-row">
<div class="setting-info">
<span class="setting-label">{$t('settings.audio.outputDevice')}</span>
@@ -3983,6 +3988,7 @@
</div>
{/if}
</div>
{#if platform === 'linux'}
{#if showAlsaPluginSelector}
<div class="setting-row">
<div class="setting-info">
@@ -4008,6 +4014,7 @@
<Toggle enabled={alsaHardwareVolume} onchange={handleAlsaHardwareVolumeChange} />
</div>
{/if}
{/if}
<div class="setting-row">
<div class="setting-info">
<span class="setting-label">{$t('settings.audio.exclusiveMode')} <span class="help-tip" title={$t('settings.audio.exclusiveModeHelp')}>(?)</span></span>
@@ -4025,6 +4032,7 @@
{#if dacPassthrough}
<small class="setting-note">{$t('settings.audio.dacPassthroughNote')}</small>
{/if}
{#if platform === 'linux'}
{#if isFlatpak && selectedBackend === 'PipeWire' && dacPassthrough}
<div class="flatpak-warning">
<div class="warning-icon">⚠️</div>
@@ -4047,6 +4055,7 @@
<small class="setting-note">{$t('settings.audio.pwForceBitperfectNote')}</small>
{/if}
{/if}
{/if}
<div class="setting-row">
<div class="setting-info">
<span class="setting-label">{$t('settings.audio.allowQualityFallback')} <span class="help-tip" title={$t('settings.audio.allowQualityFallbackHelp')}>(?)</span></span>
@@ -4369,7 +4378,7 @@
<Toggle enabled={systemNotificationsEnabled} onchange={(v) => { systemNotificationsEnabled = v; setSystemNotificationsEnabled(v); }} />
</div>
<!-- Title bar toggles: hidden on macOS (always uses native overlay title bar) -->
{#if !document.documentElement.classList.contains('macos')}
{#if platform !== 'macos'}
<div class="setting-row">
<div class="setting-info">
<span class="setting-label">{$t('settings.appearance.useSystemTitleBar')}</span>
@@ -4386,7 +4395,7 @@
</div>
{/if}
<!-- Title bar customization: hidden on macOS (uses native overlay title bar) -->
{#if !document.documentElement.classList.contains('macos')}
{#if platform !== 'macos'}
<div class="setting-row">
<div class="setting-info">
<span class="setting-label">{$t('settings.appearance.searchInTitleBar')}</span>
@@ -4573,15 +4582,15 @@
</div>
<!-- System Tray / Menu Bar subsection -->
<h4 class="subsection-title">{$t(document.documentElement.classList.contains('macos') ? 'settings.appearance.tray.titleMacos' : 'settings.appearance.tray.title')}</h4>
<h4 class="subsection-title">{$t(platform === 'macos' ? 'settings.appearance.tray.titleMacos' : 'settings.appearance.tray.title')}</h4>
<div class="setting-row">
<div class="setting-info">
<span class="setting-label">{$t(document.documentElement.classList.contains('macos') ? 'settings.appearance.tray.enableTrayMacos' : 'settings.appearance.tray.enableTray')}</span>
<span class="setting-desc">{$t(document.documentElement.classList.contains('macos') ? 'settings.appearance.tray.enableTrayDescMacos' : 'settings.appearance.tray.enableTrayDesc')}</span>
<span class="setting-label">{$t(platform === 'macos' ? 'settings.appearance.tray.enableTrayMacos' : 'settings.appearance.tray.enableTray')}</span>
<span class="setting-desc">{$t(platform === 'macos' ? 'settings.appearance.tray.enableTrayDescMacos' : 'settings.appearance.tray.enableTrayDesc')}</span>
</div>
<Toggle enabled={enableTray} onchange={(v) => handleEnableTrayChange(v)} />
</div>
{#if !document.documentElement.classList.contains('macos')}
{#if platform !== 'macos'}
<div class="setting-row">
<div class="setting-info">
<span class="setting-label">{$t('settings.appearance.tray.minimizeToTray')}</span>
@@ -4609,7 +4618,8 @@
/>
</div>
<!-- Composition subsection (collapsible) -->
<!-- Composition subsection (collapsible, Linux-only: GDK/GSK/X11/Wayland/DMA-BUF) -->
{#if platform === 'linux'}
<div class="collapsible-section composition-subsection">
<button class="section-title-btn" onclick={() => compositionCollapsed = !compositionCollapsed}>
<div class="section-title-row">
@@ -4760,6 +4770,7 @@
</div>
{/if}
</div>
{/if}
<div class="setting-row">
<div class="setting-info">
@@ -4979,8 +4990,8 @@
<section class="section">
<h3 class="section-title">{$t('settings.integrations.title')}</h3>
<!-- Qobuz Link Handler (Linux only — macOS registers via Info.plist at build time) -->
{#if !document.documentElement.classList.contains('macos')}
<!-- Qobuz Link Handler (Linux only — macOS registers via Info.plist, Windows via registry) -->
{#if platform === 'linux'}
<div class="setting-row">
<div class="setting-info">
<span class="setting-label">{$t('settings.integrations.qobuzLinkHandler')}</span>
+3 -5
View File
@@ -9,14 +9,12 @@
*/
import { invoke } from '@tauri-apps/api/core';
import { platform } from '$lib/utils/platform';
const STORAGE_KEY_HIDE = 'qbz-hide-titlebar';
const STORAGE_KEY_SYSTEM = 'qbz-use-system-titlebar';
const STORAGE_KEY_WINDOW_CONTROLS = 'qbz-show-window-controls';
// Platform detection (cached once — never changes at runtime)
const isMacOS = typeof document !== 'undefined' && document.documentElement.classList.contains('macos');
// State
let hideTitleBar = false;
let useSystemTitleBar = false;
@@ -91,7 +89,7 @@ export function getUseSystemTitleBar(): boolean {
*/
export function shouldShowTitleBar(): boolean {
// macOS always uses native decorations — no custom title bar
if (isMacOS) return false;
if (platform === 'macos') return false;
return !hideTitleBar && !useSystemTitleBar;
}
@@ -100,7 +98,7 @@ export function shouldShowTitleBar(): boolean {
* Returns 0 if title bar is hidden or system title bar is active, 40 otherwise
*/
export function getTitleBarHeight(): number {
if (isMacOS) return 0;
if (platform === 'macos') return 0;
return (hideTitleBar || useSystemTitleBar) ? 0 : 40;
}
+6 -8
View File
@@ -2,6 +2,8 @@
* Keyboard utilities for keybindings system
*/
import { platform } from '$lib/utils/platform';
/** Display mappings for special keys */
const KEY_DISPLAY_MAP: Record<string, string> = {
ArrowLeft: '←',
@@ -78,21 +80,17 @@ export function parseShortcut(shortcut: string): {
* @example formatShortcutDisplay('Ctrl+ArrowRight') // '⌘ →' on macOS, 'Ctrl + →' on others
*/
export function formatShortcutDisplay(shortcut: string): string {
const isMac =
typeof navigator !== 'undefined' &&
navigator.platform.toLowerCase().includes('mac');
const { ctrl, alt, shift, key } = parseShortcut(shortcut);
const parts: string[] = [];
if (ctrl) parts.push(isMac ? '⌘' : 'Ctrl');
if (alt) parts.push(isMac ? '⌥' : 'Alt');
if (shift) parts.push(isMac ? '⇧' : 'Shift');
if (ctrl) parts.push(platform === 'macos' ? '⌘' : 'Ctrl');
if (alt) parts.push(platform === 'macos' ? '⌥' : 'Alt');
if (shift) parts.push(platform === 'macos' ? '⇧' : 'Shift');
const displayKey = KEY_DISPLAY_MAP[key] || key.toUpperCase();
parts.push(displayKey);
return parts.join(isMac ? ' ' : ' + ');
return parts.join(platform === 'macos' ? ' ' : ' + ');
}
/**
+23
View File
@@ -0,0 +1,23 @@
/**
* Platform detection via @tauri-apps/plugin-os.
*
* `platform()` is synchronous (compile-time constant baked into the binary).
* Falls back to 'linux' in non-Tauri environments (vitest, SSR, svelte-check).
*
* NOTE: The `macos` CSS class on `<html>` is set separately by an inline
* script in app.html for FOUC prevention — that is intentionally kept.
*/
import { platform as tauriPlatform } from '@tauri-apps/plugin-os';
type Platform = 'macos' | 'linux' | 'windows';
let detectedPlatform: Platform;
try {
detectedPlatform = tauriPlatform() as Platform;
} catch {
// Fallback for non-Tauri environments (vitest, SSR, svelte-check)
detectedPlatform = 'linux';
}
export const platform: Platform = detectedPlatform;
+2 -2
View File
@@ -154,6 +154,7 @@
import { loadAlbumFavorites } from '$lib/stores/albumFavoritesStore';
import { loadArtistFavorites } from '$lib/stores/artistFavoritesStore';
import { getDefaultFavoritesTab } from '$lib/utils/favorites';
import { platform } from '$lib/utils/platform';
import type { FavoritesPreferences, ResolvedMusician } from '$lib/types';
// Navigation state management
@@ -479,7 +480,6 @@
// Title Bar State (from titleBarStore subscription)
let showTitleBar = $state(shouldShowTitleBar());
let showWindowControls = $state(getShowWindowControls());
const isMacOS = typeof document !== 'undefined' && document.documentElement.classList.contains('macos');
// Search Bar Location State
let searchBarLocationPref = $state(getSearchBarLocation());
@@ -4883,7 +4883,7 @@
{:else}
<div class="app" class:no-titlebar={!showTitleBar} class:floating={isWindowFloating}>
<!-- macOS: drag region for window movement (overlay title bar has no native drag area) -->
{#if !showTitleBar && isMacOS}
{#if !showTitleBar && platform === 'macos'}
<div class="macos-drag-region" data-tauri-drag-region></div>
{/if}
<!-- Custom Title Bar (CSD) -->