This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
# Full build (native streaming + audio visualization)
cargo run
# Slim build — no librespot/audio; fastest iteration, used by CI
cargo run --no-default-features --features telemetrycargo fmt --all
cargo clippy --no-default-features --features telemetry -- -D warnings
cargo test --no-default-features --features telemetrycargo test --no-default-features --features telemetry <test_name>
# Example:
cargo test --no-default-features --features telemetry global_shift_w_adds_current_track_from_anywhereThe codebase is split into four top-level modules under src/:
| Module | Role |
|---|---|
core/ |
Business logic & centralized state (App, UserConfig, SortState) |
infra/ |
Infrastructure: Spotify API (network/), audio capture/viz (audio/), native streaming (player/), OS integrations (Discord RPC, MPRIS, macOS media keys) |
tui/ |
Terminal UI: rendering (ui/), per-screen input handlers (handlers/), event loop (event/) |
cli/ |
CLI argument parsing and self-update logic |
Key event → tui/event/ → tui/handlers/handle_app()
↓ global keybindings
↓ handle_block_events() dispatches to per-screen handler
↓ app.dispatch(IoEvent::…) sends async work
infra/network/ fetches from Spotify API
↓ mutates App state
tui/ui/ re-renders from App state
App holds a navigation stack of Route values. Each Route contains:
RouteId— which screen to render (Home, Search, Artist, AlbumTracks, Queue, Settings, Party, …)ActiveBlock— which block currently has keyboard focusHoveredBlock— which block the cursor is hovering
Use app.push_navigation_stack(RouteId::X, ActiveBlock::X) to navigate and app.pop_navigation_stack() to go back.
The Party feature (src/infra/network/sync.rs) connects host and guests via WebSocket relay using SyncMessage enums. IoEvent::StartParty, JoinParty, SyncPlayback, and LeaveParty drive the party lifecycle from handlers.
- Add a variant to
RouteIdandActiveBlockinsrc/core/app.rs. - Create
src/tui/handlers/<screen>.rswith apub fn handler(key: Key, app: &mut App)function and register it insrc/tui/handlers/mod.rs(handle_block_eventsmatch arm). - Create
src/tui/ui/<screen>.rswith a draw function and wire it intosrc/tui/ui/mod.rs. - Add any new Spotify API calls as
IoEventvariants insrc/infra/network/mod.rsand implement them in the appropriatesrc/infra/network/<concern>.rsfile.
Call app.dispatch(IoEvent::SomeVariant) from a handler — never call async Spotify code directly from handlers or UI code.
Use ScrollableResultPages<T> (defined in src/core/app.rs) for any data that comes back page-by-page from the Spotify API.
For ScrollableResultPages<Page<T>> caches specifically:
- key and dedupe cached pages by
page.offset, not by insertion order - preserve the active visible page by offset when inserting new cached pages
- treat sparse caches as valid; next/previous page logic must target adjacent offsets, not cache index +/- 1
- keep visible table state separate from cache state; background prefetch must not append directly into
track_table.tracks - guard background prefetch with a generation/session value so stale tasks cannot write into a reloaded view
- when a page is already cached, prefer rendering it synchronously from app state instead of routing through another async event
- for playlist track tables, use
playlist_track_table_idas the current table identity;active_playlist_indexis sidebar selection state only
Show feedback with app.show_status_message(msg, ttl_ms). Do not write directly to app.status_message.
When closing a dialog, always call app.clear_playlist_track_dialog_state() alongside app.dialog = None and app.confirm = false.
Always check app.user_config.keys.<action> instead of hard-coding key literals when matching global actions (see handle_app in src/tui/handlers/mod.rs).
- Default features include
streaming(librespot) and audio visualization backends. --no-default-features --features telemetryis the minimal build used for CI and fast iteration.- Platform-specific audio backends (ALSA, PipeWire, PortAudio, Rodio) are gated behind their own features.
cover-artfeature enables album art rendering viaratatui-image.
- For native streaming (
spotatuias the active playback device), URI-list playback without a Spotify context should stay on the direct nativeplayer.load(...)path insrc/infra/network/playback.rs. - Do not reroute liked songs / saved-track playback on the active native device through Spotify Web API
start_uris_playback(..., device_id=native_device)as a recovery strategy. That path regressed first-track startup in manual testing even when the UI showed playback starting. - If native playback behavior is being changed, verify it with the full
cargo runbuild, not only the slim telemetry build.