Skip to content

v0.13.0

Choose a tag to compare

@github-actions github-actions released this 13 May 11:01

Highlights

Two PRs land in this release, both centered on the MPRIS surface:

  • #110 — Mpris fixes: a triplet of regressions on the systemd
    state cache, the MPRIS seeker, and Chrome's cover art handling.
  • #111 — Mpris UI: the dashboard's transport row moves to HTMX
    with optimistic updates and toast error reporting, MPRIS player
    cards become collapsible, a dismissible PWA promo lands in the
    header, the remaining emoji icons get replaced by inline Lucide
    SVGs, and a few more MPRIS edge cases are ironed out along the way.

✨ Features

  • Dismissible PWA promo in the header, deep-linking to the
    multi-node app at pwa.odio.love for the current host.
  • Collapsible MPRIS player cards — each player collapses to a
    single-line label, with open/closed state preserved across SSE
    re-renders.

🎨 UI

  • Transport actions unified on HTMX. Play/pause, seek, volume,
    shuffle, stop, repeat, mute and the default-sink picker all go
    through hx-post with optimistic updates; backend errors surface
    in a bottom-pinned toast rendered from the plain-text response body.
  • Lucide-style inline SVG icons everywhere — section headers,
    power, service actions, mute/play/repeat toggles, the zeroconf
    badge and the empty state. Icons inherit currentColor and toggle
    via data- attributes, no JS-side icon swap.
  • MPRIS player template split into a wrapper + _parts.gohtml for
    readability, and small MPRIS thumbnails (e.g. Chrome's 150x83
    YouTube cache) now scale up to fill the cover slot.
  • New lime accent (#5ab81e) for active-button text and
    active-service status, replacing the dim leaf/20 / green-400.

🐛 Fixes

  • MPRIS seeker no longer snaps back on Bluez/AVRCP sources that
    re-report a latched Position every poll — freshness is now tracked
    per player rather than from the global cache timestamp.
  • MPRIS cover refresh works in Chrome. Chrome keeps a stable
    trackid across tracks but rotates the /tmp art path; cache-bust
    now uses both trackid and artUrl.
  • systemd state cache no longer gets stuck on transitional
    states — the broken waitForStableState backoff (initial wait
    above the 8 s cap) is repaired and a final refresh now fires on
    timeout.
  • Service URL preserved across cache refreshes — it was getting
    dropped on every fsnotify-triggered refresh because
    serviceFromProps only sees D-Bus properties.
  • MPRIS card stops re-rendering on every 5 s position tick
    the seeker is interpolated client-side and only needs a fresh base
    when the player state actually changes.
  • Player display names use MPRIS Identity (with a cleaned-up
    bus_name fallback) instead of the lowercased / instance-suffixed
    bus name.
  • Tricky Position edge cases consolidated in a single
    shouldAcceptPosition guard: zero/negative values
    (go-librespot quirk), Bluez/AVRCP latched repeats, and
    Position == Length (Chrome on track changes).
  • Volume slider stays visible when mutedVolume is now
    *float64, so a genuine 0 still serializes as "volume": 0.

🧹 Internal

  • CI actions bumped.

Full changelog: v0.12.0...v0.13.0