Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ target
**/*.rs.bk
*.pdb

# Generated by src-tauri/build.rs based on the `updater` Cargo feature.
# Lives in capabilities/ because tauri-build auto-discovers from there;
# the file is rewritten or removed on every build to match the feature
# flag, so it must never be committed.
src-tauri/capabilities/updater.json

# TypeScript / bundler caches
*.tsbuildinfo
.eslintcache
Expand Down Expand Up @@ -72,3 +78,8 @@ secrets/
.codacy/cli-config.yaml
.codacy/logs/
.codacy/tools-configs/

# Flatpak generator cache (Python venv + downloaded generators). The
# committed outputs in packaging/flatpak/generated/ are what Flathub
# consumes; this dir is local scratch and can be regenerated anytime.
packaging/flatpak/.cache/
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ Long-form design documents that lock in cross-cutting architectural decisions be

## Contributing

[CONTRIBUTING.md](../CONTRIBUTING.md) and [RELEASING.md](../RELEASING.md) cover the contribution and release flows respectively. Anything project-wide that should always be loaded into Claude Code's context lives in [`CLAUDE.md`](../CLAUDE.md) at the repo root.
[CONTRIBUTING.md](../CONTRIBUTING.md) and [RELEASING.md](../RELEASING.md) cover the contribution and release flows respectively. [`upstream-blockers.md`](upstream-blockers.md) tracks Tauri-ecosystem issues that affect WaveFlow and the policy for handling them. Anything project-wide that should always be loaded into Claude Code's context lives in [`CLAUDE.md`](../CLAUDE.md) at the repo root.
70 changes: 70 additions & 0 deletions docs/upstream-blockers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Upstream blockers — Tauri ecosystem debt

Status snapshot — refresh the dates whenever an issue moves.

WaveFlow ships on Tauri 2 + wry + tao + tauri-bundler. Some long-standing upstream limitations affect our UX or packaging. This doc tracks them so we don't keep rediscovering the same wall, and so future-us has a defensible policy on **when to wait, patch out-of-tree, PR upstream, or walk away**.

The lazy take "let's fork the whole stack" is almost always the wrong answer — see [Strategy](#strategy) below.

## Active blockers

### B1 — webkit2gtk-4.1 / GTK3 chrome on Linux

- **Upstream**: [tauri-apps/tauri#3961](https://github.com/tauri-apps/tauri/issues/3961) (open since 2022).
- **Impact**: Linux window decorations + native dialogs render in GTK3 style, which looks dated next to libadwaita apps on GNOME 49+. Affects every distribution channel (AUR / COPR / Flatpak / .deb / .rpm / AppImage), not Flatpak-specific.
- **Severity**: Cosmetic. The app is functional; only the chrome looks off.
- **Why it's stuck**: wry's Linux backend hard-binds against the GTK3 variant of WebKitGTK. The `webkit2gtk-rs` crate now exposes both 4.1 and 6.0 APIs behind features, so the missing piece is a wry refactor that conditionally compiles against either binding (~500–1500 lines). No PR has landed yet.
- **What we can do today**: ship a custom titlebar (`decorations: false` in `tauri.conf.json` + draw it ourselves) — Spotify / VS Code / Apple Music pattern. Cross-platform side benefit. Tracked under follow-ups in PR #164.
- **What a real fix looks like**: write the wry PR ourselves or sponsor it. Triage: when one of us has a focused two-week slot.

### B2 — AppImage sidecars

- **Upstream**: [tauri-apps/tauri#2667](https://github.com/tauri-apps/tauri/issues/2667), [tauri-apps/tauri-bundler issues](https://github.com/tauri-apps/tauri/labels/scope%3A%20bundler--appimage) (open since 2022).
- **Impact**: external binaries declared via `bundle.externalBin` don't resolve cleanly inside an AppImage's squashfs mount. Affects anyone shipping ffmpeg / yt-dlp / similar as a sidecar.
- **Severity**: **N/A for WaveFlow today** — we ship zero sidecars. The audio stack (symphonia + cpal) and external API clients (reqwest) are pure Rust. Logged here so we remember this constraint if we ever consider adding one.
- **Workaround if it ever bites us**: prefer the `.deb` / `.rpm` / Flatpak channels (which all handle sidecars correctly), drop AppImage entirely, or invoke via `$PATH` and document the system dep.

### B3 — Auto-updater vs. package managers

- **Status**: resolved on our side; documented here as the canonical pattern for future channels.
- **Impact**: every distribution channel that owns its own update flow (Flatpak, MS Store, MAS) fights with `tauri-plugin-updater` over the read-only install root.
- **Our fix**: `tauri-plugin-updater` is gated behind a default-on Cargo feature (`updater`). Channels that own updates compile with `--no-default-features`. The capability file `src-tauri/capabilities/updater.json` is generated/removed by `src-tauri/build.rs` based on the feature flag so `tauri-build`'s permission validation stays in sync.

## Strategy

Decision tree when an upstream issue blocks us:

1. **Is it cosmetic and does the app still work?** → Document, ship, move on. Don't burn a sprint on chrome. (B1.)
2. **Does it affect a channel we don't actually use yet?** → Same. (B2.)
3. **Does it block a release?**
- **Trivial patch (< 100 lines)** → out-of-tree via `[patch.crates-io]` in `src-tauri/Cargo.toml`. We already do this for `glib = { path = "vendor/glib-0.18.5" }` (RUSTSEC-2024-0429 backport). Pattern: vendor the crate at the pinned version, apply the minimal diff, document in the crate's `PATCHES.md`. Drop the patch when upstream catches up.
- **Non-trivial fix (100–1500 lines)** → write the PR upstream. Maintainers are responsive to concrete, tested PRs; what they don't do is prioritize work nobody is writing. Sponsoring an outside contributor is also valid if our time is the bottleneck.
- **Architectural rewrite needed** → discuss before committing. Maintaining an out-of-tree fork of a moving ~150 kLOC subtree (`tauri` + `wry` + `tao` + `tauri-bundler`) is a 6-month tax that grows monthly. The cemetery of dead Electron / Tauri forks (Glimmr, Neutron-Native, …) is the lesson.
4. **Is the upstream project effectively unmaintained on this surface?** → That's the only honest case for a fork, and even then prefer forking the smallest possible subtree (a single crate, a single bundler module).

## Patch protocol

When B-level work justifies a `[patch.crates-io]` entry:

1. `git clone` the upstream crate at the version we depend on into `src-tauri/vendor/<crate>-<version>/`.
2. Apply the minimal diff. Commit with `vendor(<crate>): backport <upstream-pr-or-cve>`.
3. Add a `PATCHES.md` next to the crate root describing **what changed and why**, with links to the upstream PR / issue / CVE. Reviewers should be able to audit our delta against upstream in 60 seconds.
4. Wire the override in `src-tauri/Cargo.toml`:

```toml
[patch.crates-io]
<crate> = { path = "vendor/<crate>-<version>" }
```

5. Open an upstream PR with the same diff if it isn't already in flight. The patch is meant to be temporary.
6. Re-check at each Tauri minor bump: when upstream catches up, delete the vendor dir + patch entry, bump the dep, done.

## Maintenance

- Re-validate this doc at every Tauri minor bump.
- New blocker? Add a `B<n>` section above. Include the upstream issue link, severity, our workaround, and the realistic path to a real fix.
- Resolved blocker? Don't delete the section — move it to a short "Resolved" log at the bottom so we remember which PRs landed.

## Resolved

- _(nothing yet — placeholder)_
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default tseslint.config(
"vscode-discord-rich-presence/**",
"scripts/**",
"secrets/**",
"packaging/**",
"**/*.config.ts",
"**/*.config.js",
"**/.commitlintrc.cjs",
Expand Down
18 changes: 18 additions & 0 deletions packaging/flatpak/app.waveflow.WaveFlow.desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[Desktop Entry]
Type=Application
Version=1.0
Name=WaveFlow
GenericName=Music Player
GenericName[fr]=Lecteur de musique
Comment=Local-first music player with a Spotify-inspired UI
Comment[fr]=Lecteur de musique local avec une interface inspirée de Spotify
Exec=waveflow %U
Icon=app.waveflow.WaveFlow
Terminal=false
StartupNotify=true
StartupWMClass=waveflow
Categories=AudioVideo;Audio;Player;Music;
Keywords=music;player;audio;flac;mp3;dsd;library;playlist;lyrics;scrobble;equalizer;
Keywords[fr]=musique;lecteur;audio;flac;mp3;dsd;bibliothèque;playlist;paroles;scrobble;égaliseur;
MimeType=audio/flac;audio/mpeg;audio/mp4;audio/ogg;audio/opus;audio/wav;audio/x-wav;audio/x-wavpack;audio/x-aiff;audio/x-dsd;audio/x-dsf;audio/x-dff;audio/x-musepack;audio/aac;audio/aiff;application/vnd.apple.mpegurl;application/x-mpegurl;audio/x-mpegurl;audio/x-scpls;
SingleMainWindow=true
162 changes: 162 additions & 0 deletions packaging/flatpak/app.waveflow.WaveFlow.metainfo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>app.waveflow.WaveFlow</id>

<name>WaveFlow</name>
<summary>Local-first music player with a Spotify-inspired UI</summary>
<summary xml:lang="fr">Lecteur de musique local avec une interface inspirée de Spotify</summary>

<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0-only</project_license>

<developer id="dev.waveflow">
<name>InstaZDLL</name>
</developer>

<description>
<p>
WaveFlow is a local music player desktop app with a Spotify-inspired three-panel UI.
It scans your local audio folders, organizes tracks by album, artist and genre, and
plays them with a real-time audio engine — no streaming, no cloud, your music stays
on your machine.
</p>
<p>Features:</p>
<ul>
<li>Gapless playback, crossfade (static / smart album-aware / dynamic tempo-aware), ReplayGain, 6-band peaking EQ with 20 presets, playback speed 0.5×–2×</li>
<li>DSD → PCM conversion (256-tap Blackman-Harris FIR), FLAC, ALAC, MP3, AAC, OGG, Opus, WavPack and more via Symphonia</li>
<li>Real-time spectrum visualizer, fullscreen Now Playing, word-level karaoke lyrics (Enhanced LRC + TTML), sidecar .lrc / .txt auto-discovery</li>
<li>Smart playlists: Daily Mix, On Repeat, custom boolean rule trees, M3U import/export</li>
<li>Library scanner with multi-artist split, sticky compilation grouping, BLAKE3 duplicate detection, virtualised tables for 6000+ tracks</li>
<li>Deezer + Last.fm enrichment, Last.fm scrobbling, Discord Rich Presence, LRCLIB lyrics fetch, DLNA/UPnP server (opt-in)</li>
<li>WaveFlow Wrapped year-in-review, listening history, advanced search (FTS5)</li>
<li>14-preset theme system, mini-player window, immersive Now Playing, configurable keyboard shortcuts, 17-language UI</li>
<li>Multi-profile support with per-profile databases, full export/import, automatic backups</li>
</ul>
</description>

<launchable type="desktop-id">app.waveflow.WaveFlow.desktop</launchable>

<url type="homepage">https://waveflow.app</url>
<url type="bugtracker">https://github.com/InstaZDLL/WaveFlow/issues</url>
<url type="vcs-browser">https://github.com/InstaZDLL/WaveFlow</url>
<url type="donation">https://github.com/sponsors/InstaZDLL</url>

<screenshots>
<screenshot type="default">
<caption>Home view — profile-aware greeting, mood radio, Daily Mix carousel</caption>
<image>https://raw.githubusercontent.com/InstaZDLL/WaveFlow/v1.3.0/docs/screenshots/home.png</image>
</screenshot>
<screenshot>
<caption>Library — virtualised album grid with Hi-Res badges and A-Z jump</caption>
<image>https://raw.githubusercontent.com/InstaZDLL/WaveFlow/v1.3.0/docs/screenshots/library-albums.png</image>
</screenshot>
<screenshot>
<caption>Album detail — multi-disc grouping with side Now Playing panel</caption>
<image>https://raw.githubusercontent.com/InstaZDLL/WaveFlow/v1.3.0/docs/screenshots/album-detail.png</image>
</screenshot>
<screenshot>
<caption>Immersive Now Playing — full-bleed artwork with real-time spectrum visualizer</caption>
<image>https://raw.githubusercontent.com/InstaZDLL/WaveFlow/v1.3.0/docs/screenshots/immersive-view.png</image>
</screenshot>
<screenshot>
<caption>Karaoke lyrics — word-level highlight with click-to-seek</caption>
<image>https://raw.githubusercontent.com/InstaZDLL/WaveFlow/v1.3.0/docs/screenshots/karaoke-lyrics.png</image>
</screenshot>
<screenshot>
<caption>WaveFlow Wrapped — year-in-review with top tracks, average tempo, longest streak</caption>
<image>https://raw.githubusercontent.com/InstaZDLL/WaveFlow/v1.3.0/docs/screenshots/wrapped.png</image>
</screenshot>
</screenshots>

<branding>
<color type="primary" scheme_preference="light">#10b981</color>
<color type="primary" scheme_preference="dark">#064e3b</color>
</branding>

<categories>
<category>AudioVideo</category>
<category>Audio</category>
<category>Player</category>
<category>Music</category>
</categories>

<keywords>
<keyword>music</keyword>
<keyword>player</keyword>
<keyword>audio</keyword>
<keyword>flac</keyword>
<keyword>mp3</keyword>
<keyword>dsd</keyword>
<keyword>library</keyword>
<keyword>playlist</keyword>
<keyword>lyrics</keyword>
<keyword>scrobble</keyword>
<keyword>last.fm</keyword>
<keyword>equalizer</keyword>
</keywords>

<provides>
<binary>waveflow</binary>
<mediatype>audio/flac</mediatype>
<mediatype>audio/mpeg</mediatype>
<mediatype>audio/mp4</mediatype>
<mediatype>audio/ogg</mediatype>
<mediatype>audio/opus</mediatype>
<mediatype>audio/wav</mediatype>
<mediatype>audio/x-wavpack</mediatype>
<mediatype>audio/x-aiff</mediatype>
<mediatype>audio/x-dsd</mediatype>
<mediatype>audio/x-musepack</mediatype>
</provides>

<recommends>
<display_length compare="ge">768</display_length>
<internet>offline-only</internet>
</recommends>
Comment thread
InstaZDLL marked this conversation as resolved.

<supports>
<control>pointing</control>
<control>keyboard</control>
<control>touch</control>
</supports>

<content_rating type="oars-1.1" />

<releases>
<release version="1.3.0" date="2026-05-26">
<url type="details">https://github.com/InstaZDLL/WaveFlow/releases/tag/v1.3.0</url>
<description>
<p>Highlights of this release:</p>
<ul>
<li>New On Repeat smart playlist family — top ~30 tracks of the last 30 days with a rainbow-gradient cover</li>
<li>Word-level karaoke lyrics with Enhanced LRC + TTML parsing and a mot-à-mot editor</li>
<li>Automatic sidecar .lrc / .txt lyrics discovery next to audio files</li>
<li>Native OS toast notifications on track change (opt-in)</li>
<li>Unified player-bar layout panel with EQ presets and configurable cover-click action</li>
<li>Mini-player remembers position and size across launches</li>
<li>Audio pipeline popover on the quality footer (Source → Processing → Output)</li>
<li>Wrapped banner gated to the December–January season with per-year dismiss</li>
<li>Performance: parallel library tab prefetch, skeleton loaders, slimmer payloads via shared artwork base</li>
</ul>
</description>
</release>
<release version="1.2.0" date="2026-05-21">
<url type="details">https://github.com/InstaZDLL/WaveFlow/releases/tag/v1.2.0</url>
<description>
<p>Settings split into category tabs, multi-step onboarding wizard with language picker, profile delete + rename, persistent zoom with VS Code shortcuts, and a host of library/UI polish.</p>
</description>
</release>
<release version="1.1.1" date="2026-05-15">
<url type="details">https://github.com/InstaZDLL/WaveFlow/releases/tag/v1.1.1</url>
<description>
<p>Stability fixes across the library scanner, playback engine and theme system.</p>
</description>
</release>
<release version="1.0.0" date="2026-05-13">
<url type="details">https://github.com/InstaZDLL/WaveFlow/releases/tag/v1.0.0</url>
<description>
<p>Initial public release of WaveFlow.</p>
</description>
</release>
</releases>
</component>
Loading
Loading