Skip to content

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 18 Apr 23:05
· 184 commits to develop since this release

v0.3.0 — Architecture overhaul, scrolling lists, and visualizer polish

This is a large refactor release. The UI shell is split into screens + capability interfaces, the bootstrap path is fully context-aware, and lists now scroll with the mouse. There's a new visualizer, smarter device handling, and a long list of correctness fixes.

New features

  • Scrolling lists + mouse clicks — scroll the wheel anywhere in a list, click to select.
  • Marquee track titles — when the track + artist don't fit the now-playing bar, they scroll horizontally.
  • Loading spinners while data is in flight.
  • Spectrogram visualizer — new entry in the visualizer cycle (visualizer module also refactored).
  • Volume-aware visualizer gain — visualizers compensate when you change device volume so they don't go silent.
  • Device transfer preserves progress — transferring playback no longer resets the timeline.

Bug fixes

  • Search → select bug — fixed selecting albums or artists after a search.
  • Search/select track bug fixed.
  • Shuffle deselected when manually picking a track — fixed.
  • lazylist View panic — fixed the bubbles/list paginator panic when scrolling deep then filtering to a small result set (with regression tests).
  • pipe_reader deadlock — Start/Stop no longer hold the mutex while waiting on a hung read loop.
  • Auth goroutine leak — NewSavingClient now returns a cleanup func that cancels the proactive-refresh goroutine; bootstrap.Run defers it.
  • generateRandomBase64 returns an error instead of swallowing it; both call sites wrap with %w.

Architecture / refactors

This release reshapes the internals around a few invariants that are now codified in CLAUDE.md.

  • app → bootstrap — the startup package was renamed and its responsibilities tightened.
  • A root context.Context is threaded from bootstrap.Run through every Spotify call, sub-model, and view constructor. Shutdown now cancels in-flight requests instead of letting them
    run to their own timeouts. Every context.Background() in production UI code is gone.
  • Explicit construction path — ctx is the only way to build a Model, nil panics, sub-models propagate deterministically. No hidden defaults in the UI bootstrap path.
  • app.go split into smaller modules.
  • Screens send intents rather than depending on Model — communication is one-way; the shell interprets the intent.
  • Capability interfaces — new screens implement interfaces (listProvider, scrollable, clickable, enterable, searchAware, …) instead of editing centralized switches.
  • nowplaying.go, search.go, visualizers, client.go all refactored along the same lines.
  • *APIError everywhere — doWithRetry returns a structured error (Status, Body, URL) on non-2xx; bodies truncated to 500 bytes; GetPlayerState keeps its 204→nil shortcut.
  • doc.go for every internal package — packages now declare their intent and invariants in code.

CI / tooling

  • New CI lint job: gofmt -l, go vet, and golangci-lint with a conservative .golangci.yml. Every real issue surfaced was fixed (ineffectual assignment, deprecated oto.Close, three unchecked errors).
  • More linters added on top of the baseline, all findings resolved.
  • golangci-lint pinned to v1.64.8, Go targeted at 1.24.

Other

  • Device list styling fixes and improvements.
  • Improved logging across the board.
  • Added more tests (auth, audio pipe, lazylist, etc.).

Full Changelog: v0.2.0...v0.3.0