B2 phase 4, and the last of it. Measured on the device: at 424 CSS px the four configured columns fit the row *exactly* -- `--grid-cols` came out `24px 102px 101px 101px 80px` -- and not one of them fit its content, with "Duration" too narrow for its own header. The columns were never too wide; there were too many of them. So a phone draws `titleArtist` (the title with the artist under it, across the row's whole width) plus the duration, and drops the column headers and the resize handles, which are a click-to-sort and a drag with no touch equivalent. It is a **column set, not a second row template**: the row, its delegated events, the selection semantics, the playing marker and the virtualizer never learn anything changed, because from their side only the number of columns did. Three rules come with it. The row height is in two places (`PHONE_ROW_HEIGHT` and the CSS rule) and must agree, since the virtualizer positions rows from that number and a taller row overlaps its neighbour. What is drawn and what can be sorted are different questions, so the sort list is built from `configuredColumns` -- a phone has no headers either, and building it from the drawn columns would leave it able to sort by title and duration alone. And a phone's column widths are neither loaded nor saved. That third rule is the bug the device found with the arrangement already passing five component tests and five e2e specs at the phone's own viewport. `loadColumnWidths` is keyed by column *id* and fills a gap with `MIN_COLUMN_WIDTH`, so the stacked column -- which nothing can ever have saved a width for -- came out at 148px beside a duration column of 236. The mirror image was worse and unreachable from a phone at all: saving would have written those widths back under the same ids, replacing the width the user dragged on a desktop. The specs asserted shape, and the fault depended on what `localStorage` held for a different column set; the unit test now carries that map as a fixture. Verified: 809 component tests, 112 e2e specs, and on the phone at 424x439 -- `24px 304px 80px`, 52px rows, no truncation, no overflow. One full e2e run of three saw an unrelated autotag keypress spec flake and pass on retry.
YellowJacket
Music how it was meant to bee.
YellowJacket is a fast, cross-platform desktop music player for your local collection. It plays your files, keeps your library tidy, and helps you discover and organize your music — all in a clean, responsive interface. No accounts, no streaming, no telemetry: just your music on your machine.
Runs on Linux, macOS, and Windows.
Features
Play your music
- Plays MP3, FLAC, OGG Vorbis, and WAV
- Play, pause, seek, and volume control with a mute toggle
- Gapless, glitch-free seeking backed by a read-ahead buffer
- A queue you can add to, reorder, and shuffle, with play-next support
- Shuffle and repeat (off / all / one)
- Picks up right where you left off — remembers your track, position, and volume between sessions
- Media-key and MPRIS support on Linux, so your desktop's playback controls just work
Keep your library organized
- Point it at your music folders and it scans them automatically
- Reads tags and embedded cover art, and de-duplicates artwork so it isn't stored twice
- Incremental sync — only new or changed files get reprocessed, and deleted files are cleaned up
- Browse by album, artist, or genre, or search across everything
- Mark favorites and see what you've been listening to with play history
- Edit track tags directly when something's off
Playlists
- Create playlists, drag tracks in, and reorder them
- Smart playlists that build themselves from rules (by genre, rating, play count, and more)
- Pin a default playlist and spot duplicate tracks at a glance
Discover and clean up (powered by MusicBrainz)
- Explore — browse artists, releases, and genres from the MusicBrainz catalog, not just what's already in your library
- Auto-tag — match your files against MusicBrainz to fill in correct artist, album, and track metadata, with a review step before anything is written
- Lyrics search — find a track by a line you remember
Install
Download the latest build for your platform from the releases page.
| Platform | Download |
|---|---|
| Linux | yellowjacket-linux-amd64 |
| macOS | yellowjacket-darwin-universal.app.zip (Apple Silicon + Intel) |
| Windows | yellowjacket-windows-amd64.exe |
Prefer to build it yourself? See Building from source.
Getting started
- Launch YellowJacket.
- Open Settings and add the folder(s) where your music lives.
- Let the initial scan finish — you'll see progress as it works.
- Browse by album, artist, or genre, queue something up, and press play.
Your library and settings are stored locally:
| Linux / macOS | Windows | |
|---|---|---|
| Config | ~/.config/yellowjacket/ |
%LOCALAPPDATA%\yellowjacket\config |
| Library data | ~/.local/share/yellowjacket/ |
%LOCALAPPDATA%\yellowjacket\data |
Building from source
YellowJacket is built with Go and a Lit/TypeScript frontend, bridged by the Wails framework.
Prerequisites
| Tool | Version |
|---|---|
| Go | 1.25+ |
| Node.js | 22+ |
| pnpm | 10+ |
| Wails CLI | v3 — vendored, no install needed (go tool wails3) |
The Wails v3 CLI resolves from the tool block in go.mod, so there is nothing
to install globally; make setup fetches it with the rest of the tooling.
On Linux, install the system libraries Wails needs. v3 builds against GTK4 + WebKitGTK 6.0 by default:
sudo apt-get install libasound2-dev libgtk-4-dev libwebkitgtk-6.0-dev # Debian/Ubuntu
sudo pacman -S alsa-lib gtk4 webkitgtk-6.0 # Arch
A machine without webkitgtk-6.0 can still build with -tags gtk3 against the
older WebKit2GTK 4.1 stack, but that is an escape hatch, not what CI or a
release builds.
macOS and Windows need no extra system packages. Run go tool wails3 doctor to
check your environment.
Build
make setup # install tooling and git hooks
make dev # run with hot-reload
make build-prod # produce a release binary
More detail for contributors lives in
docs/dev/overview.md and CLAUDE.md.