Wrap the _position read inside untrack() in the immediate-write $effect
so it no longer registers as a reactive dependency, preventing the effect
from firing on every 4Hz position tick. Also adds three missing
restoreQueue unit tests with vi.mock for the auth store.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
M2 — Add TODO(#375) tripwire comments where the dark/light theme-color
hex pair is duplicated outside tokens.json (vite.config.ts and
applyMetaThemeColor.svelte.ts). Refactoring is out of scope for #363;
the comments are stop signs for the next person who edits these.
M5 — The SPA never reads window.__MINSTREL__.description. The OG meta
description is server-rendered and complete on its own. Drop the dead
inline-script field and the corresponding helpers in branding.ts.
Server-side BrandingConfig.Description stays — it's still used for
<meta name="description"> and og:description.
Copy nit — Playlist detail page title becomes "Playlist · Foo"
matching the singular form already used by Artist · / Album ·.
Nine golangci-lint failures accumulated across M7 #352, #372, and
6d1709c that were never surfaced because test-go never ran green long
enough to reach the lint step. The first push to land cleanly through
vet (the M7 #363 slice) ran lint and exposed them.
- errcheck: discard the error on defer Close() in collage.go,
collage_test.go, and server_test.go.
- gofmt -s: re-run on config.go and playlists_test.go.
- goimports: move the stray "time" import from the local-module group
to stdlib in server_test.go.
- revive unused-parameter: rename ctx to _ on stubScanner.Scan and
fakeLidarrUnmonitorer.UnmonitorTrack (test stubs); on
tracks.Service's adminID, add //nolint:revive directive rather than
renaming because the HTTP handler passes admin.ID (a real authed-user
UUID) and the existing comment already flags it as reserved for the
audit-log follow-up.
The legacy SMARTMUSIC_ prefix dates from when the project was specced
as "smart-music" before being renamed to Minstrel. Hard cutover to
MINSTREL_* across config.go, tests, config.example.yaml, README, and
docker-compose. Also renames SMARTMUSIC_CONFIG in cmd/minstrel/main.go
which was missed in the original audit.
Also seeds Auth.AdminBootstrap.Username = "admin" in Default() so
MINSTREL_DATABASE_URL is the only env var required for a fresh prod
deployment — the bootstrap auto-generates a password and prints it
to stderr. Drops the now-redundant MINSTREL_AUTH_ADMIN_USERNAME=admin
line from the README quickstart and docker-compose.
M1 follow-up from the #363 final review: guards
MINSTREL_BRANDING_APP_NAME and _DESCRIPTION against empty-string
overrides — empty would have shipped <title></title> to share-preview
crawlers.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add browser tab titles to all 18 static page routes using the pageTitle()
helper from $lib/branding. Titles follow the Minstrel · Section format.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Agent-authored design specs and implementation plans are kept local
on the working machine; the canonical record lives in commit messages
and the running code. The 48 historical files remain in git history
and can be retrieved via git checkout <sha> -- docs/superpowers/ if
ever needed; they just stop accruing on the dev tip.
Per-route titles, MINSTREL_APP_NAME via Go template injection of the
embedded index.html, OG/Twitter meta + theme-color follow-through for
the dark/light toggle, and an operator-first README rewrite. PWA /
service worker / install icons explicitly out of scope (Flutter #356
+ Tauri post-v1 cover mobile and desktop). OG image ships as a
generated placeholder; hand-designed artwork drops in later.
Web tokens.json was reshaped for the SPA theme toggle (#362) with
colors split into dark/light/flat blocks. gen_tokens.dart still
expected a flat string map and threw _Map<String,dynamic> cast
errors in the flutter CI workflow. Read colors.dark + colors.flat
and merge into the existing flat namespace; light variants will be
emitted when the Flutter client gains a theme toggle as part of
#356 parity work. Adds hyphen→camelCase normalization so the new
on-action token surfaces as onAction.
Those actions don't have meaningful semantics for the track you're
already listening to. New TrackMenu prop hideQueueActions gates the
pair; PlayerBar passes it true. Other mount sites (track rows in
lists) keep the full 8-entry menu.
- AddToPlaylistMenu: anchor submenu bottom to parent bottom so it
grows upward (top-0 → bottom-0) — kebab row is usually near the
bottom of the track menu and the submenu kept extending off-screen.
- HorizontalScrollRow: scrollbar-width: none + ::-webkit-scrollbar
display:none. Page arrows already drive the scroll; the scrollbar
was just consuming vertical real estate.
Spec table's "Dark (current)" column now matches the actual
tokens.json values. Added Tailwind text-action-fg alias as the
mechanism for the non-flipping --fs-on-action token. Updated
audit notes to use the real Tailwind class (text-text-primary)
rather than the raw token (text-fs-parchment).
The <img alt=""> production element renders as role="presentation"
(decorative — playlist name is in sibling text). screen.getByRole('img')
doesn't match presentation-role elements. Switch to
container.querySelector('img') so the test doesn't fight the
correct a11y choice. Production code unchanged.
svelte-check failed with 8 type errors: $app/stores' page.params.id
is typed `string | undefined`, and the page passed `id` raw to
helpers that need `string`. Two changes:
- Switch to `$app/state` (the project canonical for new pages — see
routes/albums/[id]/+page.svelte). page.params.id is a direct
property read, not a store subscription. Fall back to "" so id is
always a string; SvelteKit won't actually render the page without
a populated id, but the type-checker doesn't know that.
- Drop the now-unused `writable` import in the test file and update
the mock to expose the new state-shape (object with .params, not a
writable store).
The remaining a11y warnings (tabindex on role=menu/dialog, click
handlers without keyboard events, draggable div without role,
autofocus) are svelte-check warnings — they don't block the build.
Some are pre-existing in FlagPopover; others are tech debt from
slice 1's drag-and-drop and modal patterns. Address as a follow-up
polish task.
Forgot to include this in the previous commit (6d1709c). The new
config field's example block needs to be in the canonical yaml so
operators copying it in get the doc.
Slice 1 of M7 #352 added Server.DataDir but left it un-populated —
the playlist cover-collage writer would have used "" as the relative
path root in production, writing to the current working directory.
- Add Storage.DataDir to Config (yaml: storage.data_dir, env:
SMARTMUSIC_STORAGE_DATA_DIR), defaulting to "./data".
- server.New takes the data dir as a parameter; main.go threads it.
- main.go MkdirAll's the directory at startup so the playlist cover
writer doesn't fail on first use with a missing parent.
- config.example.yaml documents the new section.
Existing internal/server/server_test.go constructs Server directly
without server.New, so no test fixture changes needed for that file.
Header shows collage, name, description, public/private chip, track
count, owner attribution (when not owner). Edit + delete buttons
visible to the owner only. Track list uses PlaylistTrackRow with
HTML5 drag-and-drop; drop fires PUT /tracks with the new ordered
positions and TanStack Query invalidates the playlist + index cache.
Toast surface is a placeholder browser alert in slice 1 — a real
toast is a polish task whenever it lands.
Replaces the placeholder route. Two sections: "Your playlists" (owned)
and "From other users" (public). Inline create form in the header
with Enter-to-submit / Esc-to-cancel. Empty-state copy when the
operator has nothing yet. Routes to /playlists/{id} on card click via
PlaylistCard.
The "Add to playlist…" entry that #372 reserved as a disabled slot
is now active. Submenu lists the operator's own playlists
alphabetically; "New playlist…" toggles an inline create form that
makes the playlist + appends the track in two API calls.
TrackMenu's existing test asserts the entry is enabled and opens the
submenu instead of the previous "is disabled with tooltip" check.