* fix(audio): stabilize Linux output device picker and watcher
Keep pinned ALSA/cpal device ids stable when enumeration omits the active
sink or returns an equivalent name. Stop Linux device-watcher from clearing
the pin based on missing list entries; macOS and Windows still treat repeated
absence as unplugged. Settings refresh flow calls canonicalize and refetches
the list; add i18n for the out-of-list device label.
* fix(settings): sort audio output devices by label
cpal enumeration order is arbitrary; order the dropdown by readable label
and place the current OS default device first among concrete outputs.
In software compositing (WEBKIT_DISABLE_COMPOSITING_MODE=1), WebKit renders
entirely on the CPU. The mesh blob animations were not covered by the
no-compositing overrides: blob-a occupies 130×130% of the viewport, so each
frame the CPU had to composite a surface larger than the screen. On high-DPI
displays (e.g. 8K) this drops the fullscreen player to ~10 FPS.
- Stop mesh blob pan animations (static gradients are preserved)
- Stop portrait drift animation (filter was already removed)
- Remove box-shadow from the seekbar played bar (its width changes on every
playback tick; in software mode each change triggers a box-shadow repaint)
- CI: add squashfs-tools dep, APPIMAGE_EXTRACT_AND_RUN=1, appimage to
--bundles, and *.AppImage to upload glob
- main.rs: set GDK_BACKEND=x11 and WEBKIT_DISABLE_COMPOSITING_MODE=1
on Linux before Tauri init — WebKitGTK on Wayland is unstable;
both vars are overridable by setting them before launch
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add missing contributions: cucadmuh (PR #144, #167, #173, #174),
kveld9 (PR #168), nisarg-78 (PR #115)
- Remove aboutFeatures tag line from all 8 locales (too dynamic to maintain)
- Changelog section now shows only the 3 most recent versions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(settings): clearer audio device labels for duplicate ALSA names
Show HDMI outputs as "Card (HDMI n)" from hdmi:DEV indices; include PCM and
optional subdevice for hw/plughw/sysdefault; label other ALSA plugins with
iface and PCM. When labels still collide, append a structured hint
(iface · card · PCM) instead of only truncating the raw device string.
* feat(settings): improve audio output device picker
Parse ALSA-style ids into clearer labels (HDMI with DEV index, PCM/subdevice
for hw/plughw/sysdefault). Disambiguate colliding labels; share stderr
suppression for Linux device enumeration.
Add audio_default_output_device_name and tag the matching list entry as the
current system output (i18n). While the Audio tab is open, refresh list and
mark on audio:device-changed and audio:device-reset without toggling the
refresh spinner. Show an error toast if listing devices fails.
Skip column/list arrow handling when any modifier is down. Detect modifiers
via nativeEvent and getModifierState (WebKit/WebView), and match arrow keys
by both key and code. On row buttons, preventDefault for plain arrows only
to avoid native focus/scroll stealing navigation. Filter field ArrowDown uses
the same modifier check.
- Device watcher requires 3 consecutive misses (~9 s) before triggering
audio:device-reset, preventing false positives when ALSA temporarily
hides a busy device from output_devices() enumeration
- Add stderr suppression to open_stream_for_device_and_rate (last source
of ALSA terminal noise on Linux)
- Fix tooltip showing raw key 'common.refresh' — replaced with
settings.audioOutputDeviceRefresh (added to all 8 locales)
- Device switch restarts track from beginning (no seek-back)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Raw ALSA names like "sysdefault:CARD=U192k" are replaced with
readable labels ("U192k") in the device dropdown. The underlying
value stays unchanged so rodio can still open the correct device.
Covers: sysdefault, hw, plughw, iec958, front, surround prefixes.
Names without ALSA structure (pipewire, pulse, default) are kept as-is.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The module-level cache prevented newly connected USB audio devices
from appearing in the dropdown. Replace with a manual refresh button
(RotateCcw icon, spins while loading) next to the dropdown.
Device enumeration now runs on every Audio tab open and on explicit
refresh. ALSA stderr noise is already suppressed by the dup2 guard,
so re-enumeration is silent.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Device watcher (3 s loop) and audio_list_devices both called
cpal output_devices(), triggering ALSA to probe unavailable backends
(JACK, OSS, dmix) and spam stderr with error messages.
Fix: redirect fd 2 to /dev/null for the duration of each enumeration
on Unix via libc dup/dup2 + RAII guard. Also add a module-level
cache in Settings.tsx so audio_list_devices is only invoked once
per app session instead of on every Audio tab activation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds the ability to choose which audio output device Psysonic plays
through — useful for USB DACs, dedicated soundcards, and multi-device setups.
Rust (audio.rs):
- open_stream_for_device_and_rate(device_name, rate): opens a named
device by cpal name, falls back to system default if not found
- AudioEngine.selected_device: persists the chosen device across
stream reopens so hi-res rate switches stay on the right device
- audio_list_devices: Tauri command — returns all output device names
- audio_set_device: Tauri command — switches device immediately,
drops old sinks, emits audio:device-changed
- start_device_watcher: now handles two cases:
1. No pinned device + system default changed → reopen on new default
2. Pinned device disappeared (DAC unplugged) → clear selected_device,
fall back to system default, emit audio:device-reset
Frontend:
- authStore: audioOutputDevice (string | null), persisted
- playerStore: applies stored device on cold start
- App.tsx: listens to audio:device-reset, clears authStore device
and restarts playback on the fallback device
- Settings → Audio tab: device dropdown at top (above Hi-Res and EQ),
uses CustomSelect (portal-based, styled), all 8 locales
- CustomSelect: added disabled prop
Note: exclusive mode (WASAPI exclusive, CoreAudio exclusive) is out
of scope for now.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
https:// was already supported by the code (startsWith('http') check) but
the placeholder text only showed bare host:port examples, giving no hint
that a full URL with protocol is valid.
- Updated serverUrlPlaceholder in login namespace (all 8 locales) to show
https://music.example.com as the domain example
- Added settings.serverUrlPlaceholder i18n key (all 8 locales) and wired it
into AddServerForm — previously the placeholder was a hardcoded English string
Closes#171
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When offline with no cached content, the full-screen OfflineOverlay blocked
all navigation including Settings, making it impossible to fix a broken
server config. Replace it with the same slim banner used in offline-cache mode.
- OfflineBanner now handles both cases: cache (existing) and no-cache (new)
- No-cache banner shows server name and a direct link to Settings → Server tab
- OfflineOverlay component is no longer used (import removed from App.tsx)
- All 8 locales: added offlineNoCacheBanner and serverSettings keys
Fixes#170
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Multi-selection for Albums, Artists, Playlists with context menu bulk-add
- Collapsible playlist section in sidebar
- Infinite scroll on Artists page (IntersectionObserver)
- Submenu flip-up on viewport overflow
- Remove from Playlist in context menu
- All 8 locales synced
Fixes applied:
- title= → data-tooltip on playlist toggle button (CLAUDE.md)
- Hardcoded Spanish aria-label → i18n (sidebar.expandPlaylists/collapsePlaylists)
- AddToPlaylistSubmenu fetches playlists on first open if store empty
Co-Authored-By: kveld9 <kveld9@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace title= with data-tooltip on sidebar playlist expand button (CLAUDE.md)
- Replace hardcoded Spanish aria-label strings with i18n keys (sidebar.expandPlaylists / collapsePlaylists) across all 8 locales
- AddToPlaylistSubmenu now fetches playlists on first open if store is empty (regression fix)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Persist chords as ctrl/alt/shift/super plus key code; legacy single-key
bindings still match without modifiers. Settings capture uses buildInAppBinding;
App uses matchInAppBinding and skips chords also registered as global shortcuts.
Share MODIFIER_KEY_CODES and formatBinding with global shortcut formatting.
Fix seek-forward/backward hotkeys: seek() expects 0-1 progress, not seconds.
--bg-base is undefined — sticky filter bar would have rendered transparent.
Also added PR #165 to cucadmuh's contributions in Settings About.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Purple & Gold palette (Deuteranopia / Protanopia / Tritanopia safe).
Vision Dark: near-black #0D0B12 + Gold #FFD700 (~14.7:1 WCAG AAA).
Vision Navy: deep navy #0A1628 + Gold #FFD700 (~14.5:1 WCAG AAA).
New 'Accessibility' group in ThemePicker.
Removed stale theme count from aboutFeatures across all locales.
This is an initial step toward colorblind accessibility — color variables
alone cannot cover all CVD use cases. Structural improvements (secondary
indicators, pattern/shape cues) are still needed in future iterations.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add Ctrl+F filtering for the active Folder Browser column with keyboard handoff between filter and rows, and clear right-side filters when parent selection changes.
When pressing Shift+Enter on a filtered track list, append visible tracks to the queue instead of replacing the current queue.
Rust crates resolved to newer minor versions; npm pins were behind,
causing CI version-mismatch error on tauri build.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- "Diesen Song entfernen" bekommt Trash2-Icon
- Neuer Eintrag "Künstler öffnen" (goToArtist) nach "Album öffnen" in beiden Song-Kontextmenüs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- PlaylistDetail: Ctrl/Cmd+Click enters select mode; bulk drag emits
{ type: 'songs', tracks } when ≥2 selected; filtered-view rows now
also draggable as single songs
- Favorites songs: full multi-select system — Ctrl+Click, Shift+Click,
header toggle-all checkbox, bulk-selected highlight, bulk drag to
queue, bulk-bar with Add to Playlist + Clear
- ContextMenu: ArtistToPlaylistSubmenu resolves all artist album songs
and forwards to AddToPlaylistSubmenu
- Locale: common.clearSelection + playlists.addSelected in all 7 locales
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Buttons were built with inline styles — no :hover possible. Replaced with
.artists-alpha-btn CSS class: accent-colored hover with subtle glow ring,
active state unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove the 10-page cap in fetchStatisticsLibraryAggregates — the loop now runs until
the server returns a partial page, so albums/songs/playtime reflect the full library
regardless of size (previously capped at 5,000 albums). Switched sort type from
'newest' to 'alphabeticalByName' for a stable pagination order.
Add a tooltip on the Artists stat card explaining it shows album artists only (Subsonic
API limitation — track-level featured/guest artists without their own album are not
included). Tooltip added in all 8 locales. Labels with a tooltip get a dotted underline
and cursor:help as visual hint.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds full keyboard navigation (arrow keys, Enter, Ctrl+Enter for context menu) across Folder Browser columns. Context menus for all row types with keyboard-operable submenus and star-rating control via arrow keys + Enter. Now-playing path is visually emphasized and stays stable; rebuilds on hotkey re-invoke or active-path follow-along. Adaptive column layout for deep trees with right-side visibility priority. New configurable 'Open Folder Browser' keybinding. StarRating animation sync for keyboard-driven changes. All 7 locales updated.
Adds no_compositing_mode Tauri command; frontend adds html.no-compositing on Linux to replace GPU-only CSS effects (backdrop-filter, filter, mask-image) with software-friendly equivalents.
Settings → Appearance → Fullscreen Player: toggle for artist portrait visibility + 0–80% dimming slider.
Fix: long words in lyric lines now wrap correctly.
Replaces two sidebar entries (Random Mix, Random Albums) with a single 'Build a Mix' item (Wand2 icon) at /random. Landing page shows two cards — Mix by Tracks and Mix by Albums — with glow-on-hover. Old routes updated throughout. All 7 locales updated.
Replaces dedicated sort buttons with clickable column headers in album and playlist detail views. Three-click cycle: asc → desc → natural order. Sortable by title, artist, album, favorite, rating, duration. Active column shown bold with ▲/▼ indicator.