diff options
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/icon.js | 2 | ||||
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/music-app.js | 56 | ||||
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/style.css | 32 |
3 files changed, 68 insertions, 22 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/icon.js b/packages/meshbay-hub/src/meshbay_hub/static/icon.js index 87b56ee..0ecbd70 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/icon.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/icon.js @@ -74,8 +74,6 @@ const ICON_PATHS = { music: ['M9 18V5l12-2v13', 'M4 18a2 2 0 1 0 4 0 2 2 0 1 0 -4 0', 'M16 16a2 2 0 1 0 4 0 2 2 0 1 0 -4 0'], - disc: ['M3 12a9 9 0 1 0 18 0 9 9 0 1 0 -18 0', - 'M9 12a3 3 0 1 0 6 0 3 3 0 1 0 -6 0'], pause: ['M7 5.5v13', 'M17 5.5v13'], 'skip-next': ['M5 4l10 8-10 8z', 'M19 5v14'], 'skip-prev': ['M19 4L9 12l10 8z', 'M5 5v14'], diff --git a/packages/meshbay-hub/src/meshbay_hub/static/music-app.js b/packages/meshbay-hub/src/meshbay_hub/static/music-app.js index b8ee657..87e1319 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/music-app.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/music-app.js @@ -112,6 +112,48 @@ function useMusicMeta(transportRef, path, active) { return meta; } +// A drawn CD standing in for a cover nothing supplied one for -- most tiles +// in a real, older/well-ripped library land here (musicbay.md's own +// measurement: ~11% embedded art, ~26% once sibling image files are counted +// too), so this is the *default* look of the grid, not a rare fallback, and +// needed to read as a deliberate piece of art rather than a broken image. +// A flat single-color icon (the first version of this) looked exactly like +// "missing", not "no cover" -- an actual disc, with the iridescent sheen a +// real CD's data side has, reads as intentional at a glance. Genuinely +// unique gradient ids: a `<radialGradient>` id is a plain DOM id, and a grid +// full of these renders many instances at once -- reusing one literal id +// would leave every disc after the first pointing at whichever def the +// browser resolves first. +let _discIdSeq = 0; + +function DiscPlaceholder({ cls }) { + const [gradId] = useState(() => `music-disc-sheen-${_discIdSeq++}`); + return html` + <div class="${cls} music-disc-empty"> + <svg class="music-disc-svg" viewBox="0 0 100 100" aria-hidden="true"> + <defs> + <radialGradient id=${gradId} cx="36%" cy="30%" r="80%"> + <stop offset="0%" stop-color="#ffffff" stop-opacity="0.95" /> + <stop offset="14%" stop-color="#bfe3ff" stop-opacity="0.55" /> + <stop offset="32%" stop-color="#b48cf2" stop-opacity="0.42" /> + <stop offset="52%" stop-color="#f472b6" stop-opacity="0.32" /> + <stop offset="72%" stop-color="#38bdf8" stop-opacity="0.22" /> + <stop offset="100%" stop-color="#0f172a" stop-opacity="0" /> + </radialGradient> + </defs> + <circle cx="50" cy="50" r="47" fill="#161b26" /> + <circle cx="50" cy="50" r="47" fill="url(#${gradId})" /> + <circle cx="50" cy="50" r="47" fill="none" stroke="rgba(255,255,255,0.14)" stroke-width="1" /> + <circle cx="50" cy="50" r="34" fill="none" stroke="rgba(255,255,255,0.07)" stroke-width="0.6" /> + <circle cx="50" cy="50" r="25" fill="none" stroke="rgba(255,255,255,0.07)" stroke-width="0.6" /> + <circle cx="50" cy="50" r="15" fill="#e4e7ec" /> + <circle cx="50" cy="50" r="15" fill="none" stroke="rgba(0,0,0,0.18)" stroke-width="1" /> + <circle cx="50" cy="50" r="4.2" fill="#161b26" /> + </svg> + </div> + `; +} + function AlbumCard({ album, transportRef, gekRef, musicbrainzEnabled, onOpen }) { const repTrack = album.tracks.find((tr) => tr.thumb_hash) || album.tracks[0]; // Only when nothing in the library already gives us a cover -- the common @@ -122,9 +164,10 @@ function AlbumCard({ album, transportRef, gekRef, musicbrainzEnabled, onOpen }) return html` <div class="music-card" onClick=${onOpen}> - <${MediaThumb} thumbHash=${coverHash} alt=${album.album} - cls="music-cover" emptyIcon="disc" - transportRef=${transportRef} gekRef=${gekRef} /> + ${coverHash + ? html`<${MediaThumb} thumbHash=${coverHash} alt=${album.album} + cls="music-cover" transportRef=${transportRef} gekRef=${gekRef} />` + : html`<${DiscPlaceholder} cls="music-cover" />`} <div class="music-card-info"> <div class="music-card-title">${album.album}</div> <div class="music-card-sub">${album.artist}</div> @@ -153,9 +196,10 @@ function MusicDetailModal({ album, transportRef, gekRef, musicbrainzEnabled, onC </div> <div class="music-detail-body"> <div class="music-detail-header"> - <${MediaThumb} thumbHash=${coverHash} alt=${album.album} - cls="music-detail-cover" emptyIcon="disc" - transportRef=${transportRef} gekRef=${gekRef} /> + ${coverHash + ? html`<${MediaThumb} thumbHash=${coverHash} alt=${album.album} + cls="music-detail-cover" transportRef=${transportRef} gekRef=${gekRef} />` + : html`<${DiscPlaceholder} cls="music-detail-cover" />`} <div class="music-detail-meta"> <div class="music-detail-artist">${album.artist}</div> ${meta && meta.confidence ? html`<div class="music-detail-date">${meta.release_date || ''}</div>` : ''} diff --git a/packages/meshbay-hub/src/meshbay_hub/static/style.css b/packages/meshbay-hub/src/meshbay_hub/static/style.css index 6c3c0f6..3d411f7 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/style.css +++ b/packages/meshbay-hub/src/meshbay_hub/static/style.css @@ -2726,17 +2726,19 @@ a.transfer-name { display: block; background: var(--bg-surface); } -.music-cover.video-thumb-empty { aspect-ratio: 1 / 1; } -/* A big, plain disc rather than the small note icon shared with list rows — - most tiles land here (few files carry embedded/sibling art), so this is - the default look of the grid, not a rare fallback; it needs to read as - deliberate, not broken. Sized relative to the tile so it scales with - .music-grid's auto-fill columns instead of one fixed px value. */ -.music-cover.video-thumb-empty .icon { - width: 55%; - height: 55%; - color: var(--text-dim); +/* The drawn-CD placeholder (DiscPlaceholder, music-app.js) — sized relative + to the tile so it scales with .music-grid's auto-fill columns instead of + one fixed px value, and never clipped by the square tile's own overflow. */ +.music-disc-empty { + width: 100%; + aspect-ratio: 1 / 1; + display: flex; + align-items: center; + justify-content: center; + background: var(--bg-surface); + overflow: hidden; } +.music-disc-svg { width: 82%; height: 82%; } .music-card-info { padding: 8px 10px; } .music-card-title { @@ -2792,10 +2794,12 @@ a.transfer-name { flex-shrink: 0; background: var(--bg-raised); } -.music-detail-cover.video-thumb-empty .icon { - width: 55%; - height: 55%; - color: var(--text-dim); +.music-detail-cover.music-disc-empty { + width: 96px; + height: 96px; + border-radius: 6px; + flex-shrink: 0; + background: var(--bg-raised); } .music-detail-meta { min-width: 0; } .music-detail-artist { font-weight: 600; font-size: 0.95em; } |