* chore(eslint): add eslint toolchain and configs
* fix(eslint): resolve gradual-config errors (rules-of-hooks, no-empty, prefer-const, …)
* chore(eslint): clear unused vars in config, contexts, app and test
* chore(eslint): clear unused vars in api and music-network
* chore(eslint): clear unused vars in utils
* chore(eslint): clear unused vars in store
* chore(eslint): clear unused vars in cover and hooks
* chore(eslint): clear unused vars in components
* chore(eslint): clear unused vars in pages
* chore(eslint): remove explicit any in src
* chore(eslint): align react-hooks exhaustive-deps
* chore(eslint): zero gradual config on src
* chore(eslint): strict hook rules in store and utils
* chore(eslint): strict hook rules in hooks and cover
* chore(eslint): strict hook rules in components
* chore(eslint): strict hook rules in pages and contexts
* chore(eslint): document scripts ignore in eslint config
* chore(eslint): add lint script and zero strict findings
* chore(eslint): address review round 1 (gradual 0/0, per-site disable reasons)
* chore(eslint): tighten two set-state disable comments (review round 2 LOW)
* chore(nix): sync npmDepsHash with package-lock.json
* docs(changelog): add Under the Hood entry for ESLint setup (PR #1165)
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore(deps): bump esbuild to 0.28.1 for GHSA-g7r4-m6w7-qqqr
Closes Dependabot alert #17 — path traversal in esbuild dev server
(--servedir) on Windows; patched in 0.28.1.
* chore(nix): sync npmDepsHash with package-lock.json
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore(deps): bump npm patch/minor dependencies
Refresh frontend lockfile for React, Vite, Vitest, i18next, axios, Tauri
plugins, and related type packages within existing semver ranges.
* chore(deps): bump Rust patch dependencies
Update id3 to 1.17, reqwest to 0.13.4, and align root zbus with psysonic-audio 5.16.
* chore(deps): upgrade jsdom to v29
Major test-environment bump; all Vitest suites still pass and npm audit is clean.
* chore(deps): upgrade sysinfo 0.39 and zip 8
Align root sysinfo with psysonic-syncfs and migrate backup archives to zip 8.
mach2 0.6 deferred — cpal/rodio still require ^0.5.
* chore(nix): sync npmDepsHash with package-lock.json
* docs(changelog): note dependency refresh (PR #997)
* docs(changelog): move deps note to [1.48.0] section
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore(deps): batch remaining Dependabot npm bumps and ignore Symphonia 0.6
Bump @vitejs/plugin-react, @tauri-apps/cli, react-router-dom, and vitest;
configure Dependabot to skip symphonia >=0.6 and adapter-libopus >=0.3 until
the coordinated migration tracked in workdocs.
* chore(nix): sync npmDepsHash with package-lock.json
* chore: retrigger CI for PR checks
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat(settings): OpenDyslexic font option for dyslexic readers
Next step on the accessibility track. The first pass was on the colour
side — WCAG contrast audits across every theme and dedicated colour-
vision-deficiency variants for the protanopia / deuteranopia / tritan-
opia palettes. Typography is the other axis: some users with dyslexia
find a font with a heavier weighted baseline and asymmetric glyph
shapes (b/d, p/q never mirror, italic forms differentiated rather
than slanted-regular) easier to track than a typical sans.
Adds OpenDyslexic to the existing Fontsource font picker. SIL OFL
licensed, freely redistributable, and the de-facto open-source
standard for this use case. Non-variable axis, ships as four discrete
weight/style files (regular, bold, italic, bold-italic) — the Settings
picker grew an optional `hint` field on font entries so this one row
can carry a "dyslexia-friendly · no RU/ZH support" subtitle without
bloating the other 14 entries.
Latin + Latin-extended only. Cyrillic and CJK locales (RU, ZH) fall
back to the system font when this is selected; the subtitle calls out
that limitation upfront. i18n: hint string in all 8 locales
(settings.fontHintOpenDyslexic).
Accessibility is intentional product positioning here — it's an
underserved corner of the Subsonic-client ecosystem.
* chore(nix): sync npmDepsHash with package-lock.json
* docs: changelog entry for PR #507
Logs the OpenDyslexic font option in v1.46.0 "## Added".
* docs(settings): contributor entry for PR #507
Adds the OpenDyslexic accessibility bullet to Psychotoxical's
contributions list.
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: upgrade dependencies and migrate playback to rodio 0.22
Bump npm and Rust crates; adapt symphonia decoding, ringbuf 0.5, lofty tags,
and discord-rich-presence usage. Use native rodio Player/MixerDeviceSink and
cpal device descriptions; drop the unused cpal patch. Align Vite 8 build
targets and chunking; remove redundant dynamic imports and fix hot-cache debug
logging imports.
* perf(build): lazy-load routes and restore default chunk warnings
Lazy-load all routed pages with React.lazy to shrink the main bundle; wrap root
Routes in Suspense for lazy Login. Drop chunkSizeWarningLimit override so Vite
uses the default 500 kB threshold.
* fix(windows): tray double-click without spurious menu; clean unused import
Disable tray menu on left mouse-up on Windows so a double-click to hide the
main window does not immediately reopen the context menu (tray-icon default
menu_on_left_click). Gate std::fs in app_api/core behind cfg(linux) for
/proc-only code so Windows builds stay warning-free.
* fix(sidebar): preserve new-releases read state under storage cap
When merging seen album ids, keep the current newest sample first so the
500-id localStorage limit does not truncate freshly marked reads and bring
back the unread badge.
* fix(audio): hot-cache replay, analysis no-op skips, playback source UI
Retain stream_completed_cache across audio_stop so end-of-queue replay can
use RAM promote or disk hot file instead of re-ranging HTTP.
Add cpu_seed_redundant_for_track gate before file/bytes seeds and local-file
spawn; emit analysis:waveform-updated only on Upserted. Ranged/legacy promote
checks generation after await before filling the slot.
Frontend: promote on same-track and cold resume; set currentPlaybackSource on
resume, queue undo restore, and gapless track switch so cache/stream icons stay
accurate. Import tauri::Manager for try_state in audio_play.
* fix(ts): narrow activeServerId for hot-cache promote calls
promoteCompletedStreamToHotCache expects a string; bind non-null server ids
in repeat-one, playTrack prev/same-track, and cold resume paths so tauri
production build (tsc) succeeds.
* fix(player): handle same-track hot-cache promote promise chain
Add .catch for promoteCompletedStreamToHotCache → runPlayTrackBody so sync
throws and unexpected rejections do not surface as unhandled in DevTools;
reset defer-hot-cache prefetch and isPlaying on failure.
* chore(nix): sync npmDepsHash with package-lock.json
* chore(release): finalize 1.46.0 CHANGELOG with PR #463 links
Document the release with full GitHub PR #463 on every subsection so
entries stay attributable if sections are reordered. Fix ContextMenu
lines where dynamic imports were accidentally merged onto one line.
* docs(contributors): credit cucadmuh for #463
* chore(release): prepare 1.44.0 dev version and aur tag source
Set the app version to 1.44.0-dev in package manifests and update the AUR source tag template to app-v$pkgver while keeping pkgver on the current stable release.
* chore(nix): sync npmDepsHash with package-lock.json
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Supersedes PR #203's out-of-tree upstream-src layout now that the Nix
build lives in the canonical repo. The flake treats `self` as the
source of truth — nothing in flake.nix or psysonic.nix needs a manual
version bump per release.
flake.nix
- `inputs.upstream-src` dropped; `src = self` throughout
- Dev shell retains the full WebKitGTK / GStreamer / AppIndicator
closure needed for tauri:dev on NixOS (Linux x86_64 + aarch64)
nix/psysonic.nix
- Version read from package.json (upstreamMeta.version removed)
- `srcClean` filter excludes node_modules / dist / target / .git /
result / .flatpak-builder
- `cargoDeps = rustPlatform.importCargoLock` with empty
`outputHashes` — sufficient for our local `[patch.crates-io]`
path overrides (cpal-0.15.3, symphonia-format-isomp4)
- Wraps the installed binary with LD_LIBRARY_PATH + GST_PLUGIN_PATH
+ GIO_EXTRA_MODULES + GDK_BACKEND=x11 + WebKit disable flags
nix/upstream-sources.json
- Reduced to `{ npmDepsHash }`. Placeholder for the initial commit;
the verify-nix job rewrites it on every release from the actual
`prefetch-npm-deps` output.
.github/workflows/release.yml
- New `verify-nix` job gated on `create-release` (same as the
Tauri builds). Checks out `main`, installs Nix, recomputes
`npmDepsHash`, refreshes `flake.lock`, runs
`nix build .#psysonic --no-link` as the sanity check, then
commits lock + hash updates back to `main` when they changed.
- The old standalone `upstream-release-nix.yml` is intentionally
not ported; its scheduled re-poll of the upstream release is
redundant now that the source lives here.
Tarball publishing and Cachix upload remain explicitly out of scope —
those ship in a follow-up workflow tied to the cache setup.