Skip to content

UI audit: auto-update, backup, cleaner library, artwork and playlist fixes - #44

Merged
EnesYilmazcode merged 15 commits into
mainfrom
feat/polish
Sep 26, 2026
Merged

EnesYilmazcode merged 15 commits into
mainfrom
feat/polish

Conversation

@EnesYilmazcode

Copy link
Copy Markdown
Owner

Results of a full pass over every screen at iPhone size, plus the unmerged auto-update branch folded in.

From the auto-update branch (claude/phone-app-auto-update-xgckp7, minus its local tooling files):

Fixes from the audit:

  • Cover art: YouTube's default thumbnail has black bars baked in. Lists and the lock screen now use the 16:9 thumbnail cropped square, and Now Playing uses the sharp 1280px one, falling back when a video has none.
  • Playlist rows grew past the screen, so the star and menu buttons sat at a different spot on every row. Rows now truncate like the library. The bare "−" button is now a labeled "Remove from this playlist" item in the track menu.
  • Search showed two clear buttons, WebKit's own and ours.
  • The two stacked filter rows took a fifth of the Library screen. Sorting is now one tap-to-cycle button beside All/Favorites, and an empty library no longer shows filters or "0 songs".
  • Backup now includes followed podcasts, episode progress and playlist resume points. On iPhone it opens the share sheet (Save to Files), since a home-screen app can't follow a download link.

Tests: node scripts/test-series.mjs 6/6 and node scripts/test-podcasts.mjs 14/14. node scripts/shoot-audit.mjs <dir> captures every screen on the production build.

claude and others added 15 commits July 28, 2026 19:10
The app is configured with registerType:'autoUpdate', but on iOS a
standalone PWA is frozen and resumed rather than reloaded, so Safari's
built-in update check rarely fires and the app looks stale until it's
removed and re-added from the home screen. That workaround is also
risky: imported songs/playlists live in IndexedDB, which iOS can drop
when the home-screen icon is deleted.

- Register the service worker via virtual:pwa-register and call
  registration.update() on a timer and on every foreground resume, so a
  resumed iOS app picks up new builds without a reinstall.
- Request persistent storage at startup (not just on import) so the
  library is far less likely to be evicted while the app sits unused.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBLkqNYmSgVYiTwUDcdogL
Long-press anywhere on the tab bar to peek at which deploy is running
(short commit hash + build timestamp, stamped at build time via a Vite
define). The pill auto-hides after 5s and never appears in normal use,
so the UI stays clean while still making it possible to confirm that
the PWA's in-place auto-update actually landed a new build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBLkqNYmSgVYiTwUDcdogL
Research fleet -> sequential implementers -> slop-check fleet -> fresh
review -> fix/re-verify loop, parameterized by a task batch. Improves
the shared team format: batch tasks with shared research and one
combined review, self-contained reviewer, and repo-idiom-beats-house-
style as the standing rule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBLkqNYmSgVYiTwUDcdogL
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBLkqNYmSgVYiTwUDcdogL
…ring and an always-on summary

Filter the picked list down to audio files (by MIME type or the accept-list
extensions) before the quota pre-flight and the remaining counter, since iOS
Select-All sweeps in the a-Shell folder's .json sidecars and the accept attr
is advisory only. Count added tracks explicitly (the out-of-space break exits
early, so deriving it from lengths would be wrong) and always set a summary
notice after a non-empty pick: "N added", skipped/failed parts when present,
and "No new songs" when nothing changed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBLkqNYmSgVYiTwUDcdogL
Adds exportBackup/importBackup to db.js: a backup is the catalog +
playlists as plain JSON (no audio bytes, no lyrics). Restore merges
tracks by id keeping live catalog metadata and combining user state,
and merges playlists by name with fresh auto-increment ids. Restored
rows keep their original id and srcType so re-importing the same audio
file hits addLocalTrack's dedup match, which now reattaches the bytes
to the existing row when its blob is missing, keeping playlists and
stars intact (issue #40).

The Playlists tab gains quiet Export backup / Restore pills with an
inline importnote status, and the README documents updating and data
safety, replacing the now-unsafe remove-and-re-add-the-icon advice.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBLkqNYmSgVYiTwUDcdogL
UI polish pass over issue #39:

- format.js gains formatTotalDuration ('2 hr 51 min' / '48 min' / '42 sec',
  '' for empty) so views can append durations to song counts; summarize and
  formatTime are untouched.
- Library gets a Recent / Most played / A-Z sort segmented control persisted
  in localStorage (melody:librarySort, try/catch for private-mode Safari,
  pure lazy initializer for StrictMode), a song-count + duration meta line
  under the title, and a centered empty state with an icon and a second
  ImportButton CTA (only mounted while the library is empty, so the test
  harnesses' strict file-input locator still resolves on seeded dev).
- Playlist cards render a 2x2 artwork mosaic (Artwork's gradient fallback
  covers empty cells) plus a resolved song count and total duration,
  filtering dangling track ids the same way PlaylistDetail does.
- New toast in UIProvider/App replaces both inline importnote renderings
  (import summaries and backup-restore status) with a transient pill
  absolutely positioned inside the dock, keyed by id so repeat messages
  restart the 4s timer; the unused .importnote css rule is removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBLkqNYmSgVYiTwUDcdogL
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBLkqNYmSgVYiTwUDcdogL
- summarize() now says "songs" and delegates to formatTotalDuration;
  Library header, playlist cards, and playlist detail all share it, so
  the count/duration vocabulary can't drift again
- formatTotalDuration: drop the unreachable NaN check and round to
  minutes before splitting off hours, so 3599 s renders "1 hr", not
  "60 min"
- importBackup: reject backups with a schema newer than 3; drop the
  dead starred default that the spread always overwrote
- ImportButton: picking only sidecar files now says "No audio files in
  that selection" instead of claiming the songs were already imported
- PlaylistsView: export failures surface a toast instead of vanishing

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBLkqNYmSgVYiTwUDcdogL
@EnesYilmazcode
EnesYilmazcode merged commit 2691977 into main Sep 26, 2026
1 check passed
@EnesYilmazcode
EnesYilmazcode deleted the feat/polish branch September 26, 2026 03:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants