feat(web): use static SVG asset for album fallback cover
Replaces the hand-rolled inline-data-URL placeholder with a static SVG asset at /placeholders/album-fallback.svg. SvelteKit's adapter-static publishes everything in web/static/ at the URL root and the Go binary embeds the build output, so this resolves identically in dev and prod. Operator can swap the artwork by replacing the file — no code changes. Test updated to use toContain rather than strict equality, since jsdom resolves relative URLs against the test origin. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -62,11 +62,13 @@ describe('AlbumCard', () => {
|
|||||||
expect(screen.queryByText(/1959/)).not.toBeInTheDocument();
|
expect(screen.queryByText(/1959/)).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('broken cover falls back to FALLBACK_COVER data URL', async () => {
|
test('broken cover falls back to FALLBACK_COVER asset', async () => {
|
||||||
const { container } = render(AlbumCard, { props: { album } });
|
const { container } = render(AlbumCard, { props: { album } });
|
||||||
const img = container.querySelector('img') as HTMLImageElement;
|
const img = container.querySelector('img') as HTMLImageElement;
|
||||||
await fireEvent.error(img);
|
await fireEvent.error(img);
|
||||||
expect(img.src).toBe(FALLBACK_COVER);
|
// jsdom resolves relative URLs against the test origin; assert containment
|
||||||
|
// rather than strict equality to keep the test agnostic of the host.
|
||||||
|
expect(img.src).toContain(FALLBACK_COVER);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('+ queue button fetches album detail and calls enqueueTracks', async () => {
|
test('+ queue button fetches album detail and calls enqueueTracks', async () => {
|
||||||
|
|||||||
@@ -1,26 +1,10 @@
|
|||||||
// Fallback cover — an inline SVG data URL showing a beamed-eighth-notes
|
// Fallback cover used when the real cover fails to load (e.g. scanner
|
||||||
// glyph on a slate field. Used by AlbumCard / CompactTrackCard / PlayerBar's
|
// didn't pick up embedded art and there's no sidecar). Served as a static
|
||||||
// onerror handler when the real cover fails (e.g. scanner didn't pick up
|
// asset from web/static/placeholders/ so the operator can swap the artwork
|
||||||
// embedded art and there's no sidecar). Data URL avoids an extra HTTP
|
// without touching code. SvelteKit + adapter-static publishes everything
|
||||||
// round-trip; SVG keeps it crisp at any size we render.
|
// under web/static/ at the URL root, and the Go binary embeds the build
|
||||||
//
|
// output, so this resolves identically in dev and production.
|
||||||
// The glyph is two filled note heads connected by a slanted beam — bolder
|
export const FALLBACK_COVER = '/placeholders/album-fallback.svg';
|
||||||
// and more recognisable than a single-note silhouette, while staying muted
|
|
||||||
// enough to read as a placeholder rather than real content.
|
|
||||||
export const FALLBACK_COVER =
|
|
||||||
'data:image/svg+xml;utf8,' +
|
|
||||||
encodeURIComponent(
|
|
||||||
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet">
|
|
||||||
<rect width="100" height="100" fill="#1f1f23"/>
|
|
||||||
<g fill="#7a7a82">
|
|
||||||
<ellipse cx="30" cy="72" rx="10" ry="8"/>
|
|
||||||
<rect x="38" y="34" width="4" height="40"/>
|
|
||||||
<ellipse cx="68" cy="64" rx="10" ry="8"/>
|
|
||||||
<rect x="76" y="26" width="4" height="40"/>
|
|
||||||
<polygon points="38,34 80,26 80,34 38,42"/>
|
|
||||||
</g>
|
|
||||||
</svg>`
|
|
||||||
);
|
|
||||||
|
|
||||||
export function coverUrl(albumId: string): string {
|
export function coverUrl(albumId: string): string {
|
||||||
return `/api/albums/${albumId}/cover`;
|
return `/api/albums/${albumId}/cover`;
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="windows-1252"?>
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg fill="#000000" height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M503.319,5.939c-5.506-4.705-12.783-6.767-19.958-5.635L169.555,49.852c-12.04,1.901-20.909,12.28-20.909,24.47v99.097 v156.903H99.097C44.455,330.323,0,371.073,0,421.161C0,471.25,44.455,512,99.097,512c54.642,0,99.097-40.75,99.097-90.839v-66.065 V194.588l264.258-41.725v136.169h-49.548c-54.642,0-99.097,40.75-99.097,90.839s44.455,90.839,99.097,90.839 S512,429.959,512,379.871v-66.065V123.871V24.774C512,17.529,508.827,10.646,503.319,5.939z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 809 B |
Reference in New Issue
Block a user