Commit Graph
8 Commits
Author SHA1 Message Date
bvandeusenandClaude Opus 5.5 94a9c8cbe3 chore(deps): SvelteKit 3 with adapter-static 4 and TypeScript 6, full-tree npm audit (#5021)
Merges Renovate's kit 3 (PR #149) and adapter-static 4 (PR #148) bumps,
plus the migration they need. The mechanical part is `sv migrate
sveltekit-3`, run one task at a time and reviewed:

- svelte.config.js is gone. Its options move into sveltekit() in
  vite.config.ts, exported as kitOptions so vitest.config.ts runs the
  same kit setup, including the $test-utils alias the tests import.
- $lib becomes #lib through package.json "imports". There is no
  src/lib/index, so only the "#lib/*" entry is kept.
- tsconfig extends $app/tsconfig.
- Peer floors raised to kit 3's requirements: svelte ^5.57.1, vite
  ^8.0.12, svelte-check ^4.7.5.

By hand, from the codemod's list of non-automated tasks:

- goto's replaceState option is now replace; keepFocus becomes
  reset: false. For the search typeahead, reset: false also stops the
  scroll-to-top, which is wanted while typing.
- The test setup mocks drop pushState/replaceState and $app/paths
  base/assets, which kit 3 removed, and mock refreshAll in place of
  invalidateAll.
- The other flagged files only read page.url or goto internal routes,
  so they needed no change.

TypeScript goes to ^6, not the ^7 Renovate offers: kit 3 declares
typescript ^6 as a peer and svelte-check 4.7 accepts ^5 || ^6. Move to
7 once both accept it.

With Tailwind 4 and kit 3 in, `npm audit` on the whole tree reports 0,
so the web lane now audits every dependency rather than only what
ships to browsers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 10:50:39 -04:00
bvandeusen 4c49ee2cc6 feat(web): a playlist entry whose file is missing greys out and is skipped — #2527
test-web / test (push) Successful in 33s
The row treatment for a dead playlist entry already existed -- muted
text, no play on click, no drag, no kebab, never "now playing" -- but it
only fired for track_id === null, the track-deleted case. A missing file
kept a live-looking row that failed on click.

The behavioural gate now covers both, and the presentation distinguishes
them, because they mean different things to the person reading the list.
A removed track is gone for good and keeps the strikethrough. A missing
file is a track we still have -- history, likes, the lot -- whose bytes
aren't on disk right now, so it gets an explicit "File missing" and a
title explaining it stays in the playlist and comes back on its own if
the file does. A strikethrough there would claim it was deleted, which
is a lie about a file the scanner may well adopt back tomorrow (#2528).

Skipping routes through playlistTrackToRef, which already returned null
for removed tracks and whose callers already filter nulls. Adding the
unavailable check there means every queue builder -- PlaylistCard,
systemRefetch, the detail page -- skips a missing file without any of
them learning what missing_since is.

Remove stays available on a dead row: the owner must still be able to
take it out of their own list.
2026-08-16 11:55:36 -04:00
bvandeusenandClaude Opus 4.8 f7278f2417 feat(web): drop "Remove from library" from the track kebab
test-web / test (push) Successful in 32s
No single-click destructive action belongs in the kebab. Removing the item
orphaned its whole path (RemoveTrackPopover was its only caller, and the
admin/tracks API client was the popover's only caller), so per the repo's
no-dead-code convention the chain is fully removed: the menu item + its
admin/isAdmin plumbing in TrackMenu, RemoveTrackPopover(.svelte/.test),
src/lib/api/admin/tracks(.ts/.test), and the now-needless transitive mocks
in the CompactTrackCard / PlaylistTrackRow / playlist specs.

The kebab is now an 8-item, admin-agnostic menu. The DELETE /api/admin/tracks
server endpoint is untouched — a future safer admin surface can rebind it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 22:17:35 -04:00
bvandeusen 9fc21d217a feat(web): keyboard reorder on playlist tracks (Tier C10)
test-web / test (push) Successful in 31s
Drag was already wired via @neodrag/svelte; this adds the missing
keyboard path so reorder is accessible without a pointer device.

- The drag handle becomes a real <button> (focusable). Disabled
  when canDrag is false so non-owners + unavailable tracks can't
  attempt a move.
- ArrowUp / ArrowDown on the focused handle call
  onMove(row.position, row.position +/- 1). Bounds clamping
  already lives in the page-level handler (playlists/[id]/+page.svelte:39),
  so out-of-range targets resolve as no-ops.
- Matches QueueTrackRow's pattern: aria-label "Reorder track …",
  aria-keyshortcuts, swallow Space/Enter to avoid scrolling.

Test updates: drag-handle assertions retargeted to /reorder track/
+ two new ArrowUp/ArrowDown handler tests.
2026-06-01 14:16:27 -04:00
bvandeusenandClaude Opus 4.7 efa52484d4 fix(web): add player stub to player-store mocks for TrackRow tests
#401 introduced player.current?.id reads into TrackRow.svelte and
PlaylistTrackRow.svelte, breaking 26 test cases across 6 files whose
existing vi.mock('$lib/player/store.svelte', ...) blocks only stubbed
the functions used by the original components.

Added player: { current: undefined } to each affected mock — keeps
the existing function spies and lets the new isCurrent derivation
read a defined (false-y) player.current without blowing up.

Only updated the 6 files that failed; 16 other player-store mocks
exist across the suite but their tests don't render Track/Playlist
rows so the derivation never fires. Future tests that render those
rows will need the same stub (visible at the failure point with a
clear error message).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 15:05:59 -04:00
bvandeusenandClaude Opus 4.7 7e8d19606a refactor(web): sweep test files to use empty likes/quarantine mocks (#375)
Replaces duplicated inline vi.mock('$lib/api/likes', ...) blocks with
the emptyLikesMock() helper, and (where previously left in working
tree by a parallel quarantine sweep) rolls in the matching
emptyQuarantineMock() switchovers in the same files.

Together with commit dd67f28 — which already swept the four search/*
test files for likes — this completes the 18 likes-mock conversions
called out in #375.

LikeButton.test.ts kept inline — it uses a state-driven mock to
assert the toggle behavior.

SKIPPED:
- liked.test.ts: re-exports createLikedTracksInfiniteQuery and the
  album/artist variants that emptyLikesMock() doesn't provide.
- playlist.test.ts: hand-rolled { subscribe, data } stub instead of
  readable(...) — different store contract.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 15:41:15 -04:00
bvandeusen 3e34f1f7b3 refactor(web/test): default svelte-query mock in vitest.setup; remove from 34 files (B3) 2026-05-08 11:08:41 -04:00
bvandeusen 4067be04a6 feat(web): PlaylistTrackRow component for M7 #352 slice 1
Variant of TrackRow specialised for playlist detail. Drag handle
visible to owner only; remove button (X) visible to owner only;
greyed-out + strikethrough when track_id is null (upstream track
removed from library). Reuses the existing LikeButton and TrackMenu
when the track is still alive.
2026-05-03 11:16:35 -04:00