mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 23:05:46 +00:00
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)
This commit is contained in:
committed by
GitHub
parent
f3eb58c707
commit
b0f35eabc9
@@ -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.
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
"title": "Psysonic",
|
||||
"width": 1280,
|
||||
"height": 800,
|
||||
"minWidth": 360,
|
||||
"minHeight": 480,
|
||||
"minWidth": 520,
|
||||
"minHeight": 640,
|
||||
"resizable": true,
|
||||
"fullscreen": false,
|
||||
"decorations": true,
|
||||
|
||||
@@ -158,7 +158,7 @@ export default function GenreFilterBar({
|
||||
style={{ display: 'flex', alignItems: 'center', gap: '0.4rem' }}
|
||||
>
|
||||
<Filter size={14} />
|
||||
{t('common.filterGenre')}
|
||||
<span className="toolbar-btn-label">{t('common.filterGenre')}</span>
|
||||
{count > 0 && <span className="genre-filter-count">{count}</span>}
|
||||
{count > 0 && <FilterQuickClear onActiveChip onClear={clear} />}
|
||||
</button>
|
||||
|
||||
@@ -25,7 +25,7 @@ export default function LosslessFilterButton({ active, onChange }: Props) {
|
||||
}}
|
||||
>
|
||||
<Gem size={14} />
|
||||
{t('albums.losslessLabel')}
|
||||
<span className="toolbar-btn-label">{t('albums.losslessLabel')}</span>
|
||||
{active && <FilterQuickClear onActiveChip onClear={() => onChange(false)} />}
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -98,7 +98,7 @@ export default function SortDropdown<V extends string>({ value, options, onChang
|
||||
style={{ display: 'flex', alignItems: 'center', gap: '0.4rem' }}
|
||||
>
|
||||
<ArrowDownUp size={14} />
|
||||
{current?.label ?? value}
|
||||
<span className="toolbar-btn-label">{current?.label ?? value}</span>
|
||||
</button>
|
||||
|
||||
{open && createPortal(
|
||||
|
||||
@@ -62,7 +62,7 @@ export default function StarFilterButton({ active, onChange, size = 'default' }:
|
||||
}}
|
||||
>
|
||||
<Star size={14} fill={active ? 'currentColor' : 'none'} />
|
||||
{t('common.favorites')}
|
||||
<span className="toolbar-btn-label">{t('common.favorites')}</span>
|
||||
{active && <FilterQuickClear onActiveChip onClear={() => onChange(false)} />}
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -144,7 +144,7 @@ export default function YearFilterButton({
|
||||
}}
|
||||
>
|
||||
<CalendarRange size={14} />
|
||||
{active && activeLabel ? activeLabel : t('albums.yearFilterLabel')}
|
||||
<span className="toolbar-btn-label">{active && activeLabel ? activeLabel : t('albums.yearFilterLabel')}</span>
|
||||
{active && <FilterQuickClear onActiveChip onClear={clear} />}
|
||||
</button>
|
||||
|
||||
|
||||
+12
-10
@@ -365,17 +365,17 @@ export default function Albums() {
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', flexWrap: 'wrap' }}>
|
||||
{selectionMode && selectedIds.size > 0 ? (
|
||||
<>
|
||||
<button className="btn btn-surface albums-selection-action-btn" onClick={handleEnqueueSelected}>
|
||||
<button className="btn btn-surface albums-selection-action-btn" onClick={handleEnqueueSelected} data-tooltip={t('albums.enqueueSelected', { count: selectedIds.size })} data-tooltip-pos="bottom">
|
||||
<ListPlus size={15} />
|
||||
{t('albums.enqueueSelected', { count: selectedIds.size })}
|
||||
<span className="toolbar-btn-label">{t('albums.enqueueSelected', { count: selectedIds.size })}</span>
|
||||
</button>
|
||||
<button className="btn btn-surface albums-selection-action-btn" onClick={handleAddOffline}>
|
||||
<button className="btn btn-surface albums-selection-action-btn" onClick={handleAddOffline} data-tooltip={t('albums.addOffline')} data-tooltip-pos="bottom">
|
||||
<HardDriveDownload size={15} />
|
||||
{t('albums.addOffline')}
|
||||
<span className="toolbar-btn-label">{t('albums.addOffline')}</span>
|
||||
</button>
|
||||
<button className="btn btn-surface albums-selection-action-btn" onClick={handleDownloadZips}>
|
||||
<button className="btn btn-surface albums-selection-action-btn" onClick={handleDownloadZips} data-tooltip={t('albums.downloadZips')} data-tooltip-pos="bottom">
|
||||
<Download size={15} />
|
||||
{t('albums.downloadZips')}
|
||||
<span className="toolbar-btn-label">{t('albums.downloadZips')}</span>
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
@@ -422,9 +422,11 @@ export default function Albums() {
|
||||
}}
|
||||
>
|
||||
<Disc3 size={14} />
|
||||
{compFilter === 'all' ? t('albums.compilationLabel')
|
||||
: compFilter === 'only' ? t('albums.compilationOnly')
|
||||
: t('albums.compilationHide')}
|
||||
<span className="toolbar-btn-label">
|
||||
{compFilter === 'all' ? t('albums.compilationLabel')
|
||||
: compFilter === 'only' ? t('albums.compilationOnly')
|
||||
: t('albums.compilationHide')}
|
||||
</span>
|
||||
{compFilter !== 'all' && (
|
||||
<FilterQuickClear onActiveChip onClear={() => setCompFilter('all')} />
|
||||
)}
|
||||
@@ -440,7 +442,7 @@ export default function Albums() {
|
||||
style={selectionMode ? { background: 'var(--accent)', color: 'var(--ctp-crust)' } : {}}
|
||||
>
|
||||
<CheckSquare2 size={15} />
|
||||
{selectionMode ? t('albums.cancelSelect') : t('albums.select')}
|
||||
<span className="toolbar-btn-label">{selectionMode ? t('albums.cancelSelect') : t('albums.select')}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user