diff --git a/CHANGELOG.md b/CHANGELOG.md index 19cabc4e..3a87583c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -156,6 +156,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Fifty strings that still fell back to English in the Russian UI are now translated — macOS in-place updater, device sync file migration, fullscreen lyrics, and statistics share-image export. * User-facing descriptions in Russian and English no longer mention WebKitGTK or Fisher–Yates internals; several Russian labels and section titles read more naturally (settings casing, smart playlists, track transitions, and home rails). +### macOS — themed window title bar + +**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#1199](https://github.com/Psychotoxical/psysonic/pull/1199)**, suggested by [@bcorporaal](https://github.com/bcorporaal) + +* On macOS the window's title bar now follows the active theme instead of the grey system bar; the native macOS window buttons stay in place, floating over the themed bar. + ## Fixed diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 5c5a2550..38413415 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -22,6 +22,7 @@ "fullscreen": false, "decorations": true, "transparent": false, + "titleBarStyle": "Overlay", "visible": false, "backgroundColor": "#1e1e2e", "dragDropEnabled": false diff --git a/src/app/AppShell.tsx b/src/app/AppShell.tsx index 8368e6b8..92bc40d8 100644 --- a/src/app/AppShell.tsx +++ b/src/app/AppShell.tsx @@ -56,7 +56,7 @@ import { useNowPlayingPrewarm } from '../hooks/useNowPlayingPrewarm'; import { useOfflineAutoNav } from '../hooks/useOfflineAutoNav'; import { useOfflineLibraryFilterSuspend } from '../hooks/useOfflineLibraryFilterSuspend'; import { AppShellQueueResizerSeam } from '../components/AppShellQueueResizerSeam'; -import { IS_LINUX } from '../utils/platform'; +import { IS_LINUX, IS_MACOS } from '../utils/platform'; import { useConnectionStatus } from '../hooks/useConnectionStatus'; import { useIdlePlayQueuePull } from '../hooks/useIdlePlayQueuePull'; import { useAuthStore } from '../store/authStore'; @@ -75,7 +75,7 @@ import { } from '../utils/componentHelpers/appShellHelpers'; /** - * The main webview's persistent layout: titlebar (Linux only) + sidebar + + * The main webview's persistent layout: titlebar (Linux + macOS) + sidebar + * main content area (header + route host + offline banner) + queue panel + * player bar + fullscreen overlay + global modals + tray-tooltip / title * sync. Mounted under `` and shared across all routes. @@ -226,12 +226,22 @@ export function AppShell() { const isMobilePlayer = isMobile && location.pathname === '/now-playing'; + // Custom in-page titlebar. Linux: opt-in, native decorations off. macOS: + // always on — `titleBarStyle: Overlay` lets the webview reach the top edge + // with the native traffic lights floating over our themed bar, so the bar + // follows the active theme instead of the grey system titlebar (#1198). + // Hidden in native fullscreen (the OS chrome is gone there anyway). + const showLinuxTitlebar = IS_LINUX && useCustomTitlebar && !isWindowFullscreen && !isTilingWm; + const showMacTitlebar = IS_MACOS && !isWindowFullscreen; + const showTitlebar = showLinuxTitlebar || showMacTitlebar; + return (
e.preventDefault()} > - {IS_LINUX && useCustomTitlebar && !isWindowFullscreen && !isTilingWm && } + {showTitlebar && } {import.meta.env.DEV && isMobile && ( DEV )} diff --git a/src/components/TitleBar.tsx b/src/components/TitleBar.tsx index 3c858edf..bbde192a 100644 --- a/src/components/TitleBar.tsx +++ b/src/components/TitleBar.tsx @@ -3,6 +3,7 @@ import { getCurrentWindow } from '@tauri-apps/api/window'; import { Minus, Square, X } from 'lucide-react'; import { usePlayerStore } from '../store/playerStore'; import { useAuthStore } from '../store/authStore'; +import { IS_MACOS } from '../utils/platform'; export default function TitleBar() { const win = getCurrentWindow(); @@ -13,6 +14,11 @@ export default function TitleBar() { return (
+ {/* macOS drops the now-playing label: subpixel antialiasing is disabled + in the Overlay title-bar zone, so small text renders frayed. The bar + stays a clean themed strip with the native traffic lights (#1198); + the track is already shown in the player bar. */} + {!IS_MACOS && (
{currentTrack && ( <> @@ -23,7 +29,11 @@ export default function TitleBar() { )}
+ )} + {/* macOS keeps its native traffic lights (floating over the bar via + titleBarStyle: Overlay); only Linux draws in-page window buttons. */} + {!IS_MACOS && (
{showMinimizeButton && (
+ )}
); } diff --git a/src/config/settingsCredits.ts b/src/config/settingsCredits.ts index 76b82889..a92409db 100644 --- a/src/config/settingsCredits.ts +++ b/src/config/settingsCredits.ts @@ -392,6 +392,7 @@ const CONTRIBUTOR_ENTRIES = [ 'Compact buttons — appearance toggle that switches action and toolbar buttons to icon-only across detail headers and browse views, app-wide (PR #1189)', 'Playlist sort — sort a playlist by date added (newest/oldest) or any track column from a visible sort dropdown (PR #1191)', 'Configurable artist backdrops — per-place source order + on/off (mainstage hero, artist page, fullscreen), with the mainstage hero showing the artist backdrop and prefetching upcoming ones (PR #1193)', + 'Themed window title bar on macOS — follows the active theme instead of the grey system bar, with the native window buttons floating over it (PR #1199)', ], }, { diff --git a/src/styles/layout/custom-title-bar-linux-only-decorations-false.css b/src/styles/layout/custom-title-bar.css similarity index 87% rename from src/styles/layout/custom-title-bar-linux-only-decorations-false.css rename to src/styles/layout/custom-title-bar.css index ea877c41..f1fbd99c 100644 --- a/src/styles/layout/custom-title-bar-linux-only-decorations-false.css +++ b/src/styles/layout/custom-title-bar.css @@ -1,4 +1,4 @@ -/* ─── Custom title bar (Linux only — decorations: false) ─── */ +/* ─── Custom title bar — Linux (decorations: false) + macOS (titleBarStyle: Overlay) ─── */ :root { --titlebar-height: 32px; } @@ -19,9 +19,10 @@ "sidebar main queue"; } -/* Resize grips — replace the native GTK grips lost when decorations: false */ -.app-shell[data-titlebar]::before, -.app-shell[data-titlebar]::after { +/* Resize grips — Linux only; replace the native GTK grips lost when + decorations: false. macOS keeps its native window-edge resize. */ +.app-shell[data-titlebar-platform='linux']::before, +.app-shell[data-titlebar-platform='linux']::after { content: ''; position: fixed; bottom: 0; @@ -33,8 +34,8 @@ opacity: 0.35; z-index: 9999; } -.app-shell[data-titlebar]::before { left: 0; } -.app-shell[data-titlebar]::after { right: 0; } +.app-shell[data-titlebar-platform='linux']::before { left: 0; } +.app-shell[data-titlebar-platform='linux']::after { right: 0; } .titlebar { grid-area: titlebar; @@ -50,8 +51,14 @@ .titlebar-track { position: absolute; - left: 50%; - transform: translateX(-50%); + /* Centre without translateX: a transform promotes the label to its own + compositing layer, which drops macOS subpixel antialiasing and lands it + on half-pixels — the text ends up frayed/grainy. Auto-margins between + left:0/right:0 keep it centred and pixel-snapped on both platforms. */ + left: 0; + right: 0; + width: fit-content; + margin-inline: auto; display: flex; align-items: center; gap: 6px; diff --git a/src/styles/layout/index.css b/src/styles/layout/index.css index c8271bf0..7e88200a 100644 --- a/src/styles/layout/index.css +++ b/src/styles/layout/index.css @@ -1,6 +1,6 @@ @import './_intro.css'; @import './dev-build-chrome.css'; -@import './custom-title-bar-linux-only-decorations-false.css'; +@import './custom-title-bar.css'; @import './resizer-handles.css'; @import './sidebar.css'; @import './update-toast.css';