feat: add rustify-tui terminal music player with 3-tier feature set#2
feat: add rustify-tui terminal music player with 3-tier feature set#2
Conversation
Design spec for a rich terminal music player built on rustify-core. Covers architecture (ratatui + crossbeam event loop), sidebar+main layout, library browsing, playlist management, and album art rendering. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
16-task plan covering crate scaffolding, event system, app state, UI layout (sidebar + main panel + now-playing), library indexing, player integration, playlist/queue management, and mouse support. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds config, event system, app state, library index, UI layout (sidebar, main panel, now-playing bar), player integration, queue/playlist management, search, mouse support, and status bar. 44 tests passing across all modules. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Covers shuffle/repeat modes, gapless playback via dual-decode mixer, seek keybindings, and album art extraction/rendering. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7-task plan covering shuffle/repeat in Tracklist, Player API, TUI keybindings, album art extraction, and gapless dual-decode mixer. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Core: RepeatMode enum, Fisher-Yates shuffle in Tracklist, Player API for set_shuffle/set_repeat with ModeChanged callbacks. TUI: s/r keybindings for shuffle/repeat, left/right arrows for ±5s seek, [S]/[R]/[R1] indicators in now-playing bar. 110 tests passing across workspace. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Core: new art.rs module extracts cover art from embedded tags (lofty) with sidecar file fallback (cover.jpg, folder.jpg, etc). TUI: background art loading on track change, art placeholder in now-playing bar with mode indicators [S]/[R]/[R1]. 114 tests passing across workspace. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds TrackEnding event when decode nears end (~3s remaining), pre-starts next decode thread, and swaps audio channels in the cpal callback for seamless track transitions. The MixStage architecture supports future crossfade (Tier 3) by design. 114 tests passing across workspace. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Covers audio spectrum visualizer (FFT + waveform in now-playing bar), color theme system (5 presets + custom TOML themes), and fuzzy search with nucleo-matcher and match highlighting. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4-task plan: color themes, fuzzy search, core sample buffer, and audio spectrum visualizer with FFT + waveform modes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New theme.rs module with default, nord, dracula, gruvbox, catppuccin presets. All UI modules now use theme colors instead of hardcoded values. Custom themes via [theme.custom] in tui.toml. 119 tests passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tcher Library::fuzzy_search() returns ranked results with match scores and highlighted character indices. 123 tests passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Covers crossfade (extends dual-decode mixer), MPRIS media keys (Linux D-Bus, feature-gated), ListenBrainz scrobbling, replay gain normalization, and lyrics display (embedded tags + .lrc sidecar). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Widen track info column, add ellipsis for long album names - Replace chunky Gauge with thin unicode progress bar (━/━) - Increase visualizer from 24 to 40 bars - Add sqrt scaling for better frequency visibility Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- docs/TUI.md: complete documentation of features, keybindings, config, architecture, what was built, and what's next - rules/DEVELOPMENT.md: code patterns, file organization, testing, commit style, Pi constraints - rules/CLAUDE.md: session guidelines for future Claude interactions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 099613d244
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let handle = thread::Builder::new() | ||
| .name("rustify-decode-pending".into()) | ||
| .spawn(move || { | ||
| decode_thread(uri, pending_tx, control_rx, event_tx, crossfade_ms); | ||
| }) |
There was a problem hiding this comment.
Isolate pending decoder events from active playback
When gapless prebuffering starts, this spawns a full decode_thread on the same event_tx before the current track has ended. decode_thread emits TrackChanged immediately and can emit DecodeFailed, so the UI/scrobbler can switch to the next song several seconds early (prebuffer window) and a failure to open the next track can incorrectly force the player into Stopped while the current track is still playing. Pending decoders need separate/internal events until promotion at TrackEnded.
Useful? React with 👍 / 👎.
| self.pending_decode = Some(DecodeHandle { | ||
| control_tx, | ||
| _thread: handle, | ||
| }); | ||
|
|
There was a problem hiding this comment.
Cancel pending gapless decoder on transport resets
A pending decoder handle is stored here, but reset paths (handle_stop, handle_next, LoadTrackUris, etc.) only stop decode_handle and never tear down pending_decode/its queued audio. If a user stops or skips during the prebuffer window, that stale pending stream can still be promoted/consumed later, leading to unexpected playback of the wrong track after a stop/queue change.
Useful? React with 👍 / 👎.
| if config.crossfade_ms > 0 { | ||
| player.set_crossfade(config.crossfade_ms); | ||
| } |
There was a problem hiding this comment.
Wire replay-gain config into playback path
The config now exposes replay_gain, and core adds replay-gain metadata parsing, but startup only applies crossfade_ms and never reads or applies replay_gain. In practice, enabling replay gain in tui.toml has no effect, so loudness normalization is silently non-functional despite the new setting.
Useful? React with 👍 / 👎.
Resolve all clippy -D warnings: unused imports, dead code, manual range contains, loop variable indexing. Apply cargo fmt formatting. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add .claude/, .clone/, .superpowers/ to .gitignore. Remove accidentally tracked worktree and brainstorm files. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Gapless TrackChanged timing: pending decoders now emit PendingTrackReady instead of TrackChanged. Metadata is held until promotion at TrackEnded, preventing early UI/scrobbler switch. PendingDecodeFailed discards the pending decoder silently without affecting the current track. 2. Pending decode cleanup: added stop_pending_decode() method that tears down pending_decode, clears pending_track, and removes the pending audio slot. Called from stop_decode(), which is used by handle_stop, handle_next, LoadTrackUris, and ClearTracklist. 3. Replay gain wiring: config.replay_gain is now read at startup and applied on TrackChanged. Reads REPLAYGAIN_TRACK_GAIN tag, computes gain factor (clamped 0.1x-2.0x), adjusts volume relative to base_volume. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
crates/rustify-tui) built onrustify-corewith ratatui + crosstermdocs/superpowers/Tier 1: Playback Essentials
Tier 2: Rich Experience
Tier 3: Power User
Documentation
docs/TUI.md— full feature docs, keybindings, config, architecturerules/DEVELOPMENT.md— code patterns and guidelinesrules/CLAUDE.md— session guidelines for future developmentTest plan
cargo test --workspacepasses (139 tests)cargo run -p rustify-tui -- /path/to/musiclaunches TUI, plays audio/finds tracks🤖 Generated with Claude Code