From b0f35eabc9b94618f34252323c1bd1030a56ea0a Mon Sep 17 00:00:00 2001 From: Frank Stellmacher <171614930+Psychotoxical@users.noreply.github.com> Date: Thu, 4 Jun 2026 11:16:48 +0200 Subject: [PATCH] fix: usable layout when the window is small (#981) * fix(layout): collapse browse toolbar to icons on compact width On a narrow window the labelled filter buttons wrapped into several rows and, being a fixed-height sticky header, starved the album grid below them down to a clipped strip. Wrap each toolbar label in a hideable span and drop to icon-only in compact (mobile) mode; icons keep their tooltip and aria-label so the action stays discoverable. * fix(window): raise minimum window size to 520x640 The old 360x480 floor let the window shrink far past the point the layout holds together. Floor it at a laptop-friendly size where the compact layout (icon toolbar + scrollable lists) still works. * fix(player): scale mobile cover to fit short windows The cover width was viewport-derived with no height bound, so on a short window the square grew past its slot and overlapped the title. Bind it to the shrinkable wrap via max-height and keep it square with auto sizing. * docs: changelog for small-window layout fixes (#981) --- CHANGELOG.md | 8 ++++++++ src-tauri/tauri.conf.json | 4 ++-- src/components/GenreFilterBar.tsx | 2 +- src/components/LosslessFilterButton.tsx | 2 +- src/components/SortDropdown.tsx | 2 +- src/components/StarFilterButton.tsx | 2 +- src/components/YearFilterButton.tsx | 2 +- src/pages/Albums.tsx | 22 ++++++++++++---------- src/styles/layout/cover-art.css | 11 +++++++++-- src/styles/layout/main-content.css | 9 +++++++++ 10 files changed, 45 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cca34cb..4db1811f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -885,6 +885,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Suggested Songs now show the favorite heart and star rating like the playlist above, so the Favorite/Rating columns no longer leave an empty gap. * Tracks with several artists split into individually clickable names, matching the rest of the app — and reading the same before and after you add the track. +### Small windows — usable layout when scaled down + +**By [@Psychotoxical](https://github.com/Psychotoxical), reported by zunoz on Discord, PR [#981](https://github.com/Psychotoxical/psysonic/pull/981)** + +* Browse toolbars (Albums, Artists, …) collapse their filter buttons to icons on a narrow window instead of wrapping into rows that pushed the list off-screen; hover or focus still shows each button's label. +* The minimum window size is a touch larger so the layout can no longer be shrunk past the point where it breaks. +* On a short window the Now Playing cover scales down to fit instead of overlapping the track title. + ## [1.46.0] - 2026-05-18 > **🙏 Special thanks to [@zz5zz](https://github.com/zz5zz)** for his tireless quirk-spotting and bug reports on the [Psysonic Discord](https://discord.gg/AMnDRErm4u) — several of the polish fixes in this release landed directly off the back of his messages. diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index b36e9b5c..37a1f0da 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -16,8 +16,8 @@ "title": "Psysonic", "width": 1280, "height": 800, - "minWidth": 360, - "minHeight": 480, + "minWidth": 520, + "minHeight": 640, "resizable": true, "fullscreen": false, "decorations": true, diff --git a/src/components/GenreFilterBar.tsx b/src/components/GenreFilterBar.tsx index 309e3eb6..85bb94be 100644 --- a/src/components/GenreFilterBar.tsx +++ b/src/components/GenreFilterBar.tsx @@ -158,7 +158,7 @@ export default function GenreFilterBar({ style={{ display: 'flex', alignItems: 'center', gap: '0.4rem' }} > - {t('common.filterGenre')} + {t('common.filterGenre')} {count > 0 && {count}} {count > 0 && } diff --git a/src/components/LosslessFilterButton.tsx b/src/components/LosslessFilterButton.tsx index e38d4f53..3d87e87b 100644 --- a/src/components/LosslessFilterButton.tsx +++ b/src/components/LosslessFilterButton.tsx @@ -25,7 +25,7 @@ export default function LosslessFilterButton({ active, onChange }: Props) { }} > - {t('albums.losslessLabel')} + {t('albums.losslessLabel')} {active && onChange(false)} />} ); diff --git a/src/components/SortDropdown.tsx b/src/components/SortDropdown.tsx index 0a0790eb..d4a118c8 100644 --- a/src/components/SortDropdown.tsx +++ b/src/components/SortDropdown.tsx @@ -98,7 +98,7 @@ export default function SortDropdown({ value, options, onChang style={{ display: 'flex', alignItems: 'center', gap: '0.4rem' }} > - {current?.label ?? value} + {current?.label ?? value} {open && createPortal( diff --git a/src/components/StarFilterButton.tsx b/src/components/StarFilterButton.tsx index f4c2e3e6..21b75434 100644 --- a/src/components/StarFilterButton.tsx +++ b/src/components/StarFilterButton.tsx @@ -62,7 +62,7 @@ export default function StarFilterButton({ active, onChange, size = 'default' }: }} > - {t('common.favorites')} + {t('common.favorites')} {active && onChange(false)} />} ); diff --git a/src/components/YearFilterButton.tsx b/src/components/YearFilterButton.tsx index 3209d719..40c63598 100644 --- a/src/components/YearFilterButton.tsx +++ b/src/components/YearFilterButton.tsx @@ -144,7 +144,7 @@ export default function YearFilterButton({ }} > - {active && activeLabel ? activeLabel : t('albums.yearFilterLabel')} + {active && activeLabel ? activeLabel : t('albums.yearFilterLabel')} {active && } diff --git a/src/pages/Albums.tsx b/src/pages/Albums.tsx index b8b163cf..f6b689af 100644 --- a/src/pages/Albums.tsx +++ b/src/pages/Albums.tsx @@ -365,17 +365,17 @@ export default function Albums() {
{selectionMode && selectedIds.size > 0 ? ( <> - - - ) : ( @@ -422,9 +422,11 @@ export default function Albums() { }} > - {compFilter === 'all' ? t('albums.compilationLabel') - : compFilter === 'only' ? t('albums.compilationOnly') - : t('albums.compilationHide')} + + {compFilter === 'all' ? t('albums.compilationLabel') + : compFilter === 'only' ? t('albums.compilationOnly') + : t('albums.compilationHide')} + {compFilter !== 'all' && ( setCompFilter('all')} /> )} @@ -440,7 +442,7 @@ export default function Albums() { style={selectionMode ? { background: 'var(--accent)', color: 'var(--ctp-crust)' } : {}} > - {selectionMode ? t('albums.cancelSelect') : t('albums.select')} + {selectionMode ? t('albums.cancelSelect') : t('albums.select')}
diff --git a/src/styles/layout/cover-art.css b/src/styles/layout/cover-art.css index 315c30db..0f9564b4 100644 --- a/src/styles/layout/cover-art.css +++ b/src/styles/layout/cover-art.css @@ -8,8 +8,15 @@ } .mp-cover { - width: min(calc(100vw - 28px), 55vh); - max-width: 440px; + /* Largest square that fits BOTH the viewport width and the height the flex + column leaves for the cover. `max-height: 100%` ties the image to its + (shrinkable) wrap, so on a short window it scales down instead of growing + past its slot and overlapping the title (zunoz, RC2 small-window report). + `width/height: auto` + `aspect-ratio` keep it square while it shrinks. */ + max-width: min(calc(100vw - 28px), 440px); + max-height: 100%; + width: auto; + height: auto; aspect-ratio: 1; object-fit: cover; border-radius: 14px; diff --git a/src/styles/layout/main-content.css b/src/styles/layout/main-content.css index 09c9079b..c0399614 100644 --- a/src/styles/layout/main-content.css +++ b/src/styles/layout/main-content.css @@ -180,6 +180,15 @@ min-height: 0; } +/* Compact mode: the sticky toolbar is `flex-shrink: 0`, so labelled filter + buttons wrap into several rows on a narrow window and starve the `flex: 1` + list below them, clipping the album grid (zunoz, RC2). Collapse the toolbar + buttons to icon-only (label hidden, icon + tooltip + aria-label kept) so the + row stays short and the list keeps its height. */ +.app-shell[data-mobile] .mainstage-inpage-toolbar .toolbar-btn-label { + display: none; +} + .mainstage-inpage-scroll__viewport { padding-bottom: var(--space-6); padding-right: var(--space-6);