cc
This commit is contained in:
parent
7f90a278fa
commit
0f1a9841d1
11 changed files with 1550 additions and 1185 deletions
|
|
@ -122,7 +122,7 @@
|
|||
|
||||
<div id="page-album" class="page">
|
||||
<header class="detail-header">
|
||||
<img id="album-detail-image" src="" alt="Album Art" class="detail-header-image">
|
||||
<img id="album-detail-image" src="" alt="" class="detail-header-image">
|
||||
<div class="detail-header-info">
|
||||
<div class="type">Album</div>
|
||||
<h1 class="title" id="album-detail-title"></h1>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
//js/api.js
|
||||
import { RATE_LIMIT_ERROR_MESSAGE, deriveTrackQuality, delay } from './utils.js';
|
||||
import { APICache } from './cache.js';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
//js/app.js
|
||||
import { LosslessAPI } from './api.js';
|
||||
import { apiSettings } from './storage.js';
|
||||
import { UIRenderer } from './ui.js';
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
//js/cache.js
|
||||
export class APICache {
|
||||
constructor(options = {}) {
|
||||
this.memoryCache = new Map();
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
//js/player.js
|
||||
import { REPEAT_MODE, SVG_PLAY, SVG_PAUSE, formatTime } from './utils.js';
|
||||
|
||||
export class Player {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
//js/storage.js
|
||||
export const apiSettings = {
|
||||
STORAGE_KEY: 'monochrome-api-instances',
|
||||
defaultInstances: [
|
||||
|
|
|
|||
98
js/ui.js
98
js/ui.js
|
|
@ -45,6 +45,53 @@ export class UIRenderer {
|
|||
`;
|
||||
}
|
||||
|
||||
createSkeletonTrack(showCover = false) {
|
||||
return `
|
||||
<div class="skeleton-track">
|
||||
<div class="skeleton skeleton-track-number"></div>
|
||||
<div class="skeleton-track-info">
|
||||
${showCover ? '<div class="skeleton skeleton-track-cover"></div>' : ''}
|
||||
<div class="skeleton-track-details">
|
||||
<div class="skeleton skeleton-track-title"></div>
|
||||
<div class="skeleton skeleton-track-artist"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="skeleton skeleton-track-duration"></div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
createSkeletonCard(isArtist = false) {
|
||||
return `
|
||||
<div class="skeleton-card ${isArtist ? 'artist' : ''}">
|
||||
<div class="skeleton skeleton-card-image"></div>
|
||||
<div class="skeleton skeleton-card-title"></div>
|
||||
<div class="skeleton skeleton-card-subtitle"></div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
createSkeletonDetailHeader(isArtist = false) {
|
||||
return `
|
||||
<div class="skeleton-detail-header">
|
||||
<div class="skeleton skeleton-detail-image ${isArtist ? 'artist' : ''}"></div>
|
||||
<div class="skeleton-detail-info">
|
||||
<div class="skeleton skeleton-detail-type"></div>
|
||||
<div class="skeleton skeleton-detail-title"></div>
|
||||
<div class="skeleton skeleton-detail-meta"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
createSkeletonTracks(count = 5, showCover = false) {
|
||||
return `<div class="skeleton-container">${Array(count).fill(0).map(() => this.createSkeletonTrack(showCover)).join('')}</div>`;
|
||||
}
|
||||
|
||||
createSkeletonCards(count = 6, isArtist = false) {
|
||||
return `<div class="card-grid">${Array(count).fill(0).map(() => this.createSkeletonCard(isArtist)).join('')}</div>`;
|
||||
}
|
||||
|
||||
renderListWithTracks(container, tracks, showCover) {
|
||||
container.innerHTML = tracks.map((track, i) =>
|
||||
this.createTrackItemHTML(track, i, showCover)
|
||||
|
|
@ -93,9 +140,9 @@ async renderSearchPage(query) {
|
|||
const artistsContainer = document.getElementById('search-artists-container');
|
||||
const albumsContainer = document.getElementById('search-albums-container');
|
||||
|
||||
tracksContainer.innerHTML = createPlaceholder('Searching...', true);
|
||||
artistsContainer.innerHTML = createPlaceholder('Searching...', true);
|
||||
albumsContainer.innerHTML = createPlaceholder('Searching...', true);
|
||||
tracksContainer.innerHTML = this.createSkeletonTracks(8, false);
|
||||
artistsContainer.innerHTML = this.createSkeletonCards(6, true);
|
||||
albumsContainer.innerHTML = this.createSkeletonCards(6, false);
|
||||
|
||||
try {
|
||||
const [tracksResult, artistsResult, albumsResult] = await Promise.all([
|
||||
|
|
@ -162,15 +209,32 @@ async renderSearchPage(query) {
|
|||
|
||||
async renderAlbumPage(albumId) {
|
||||
this.showPage('album');
|
||||
|
||||
const imageEl = document.getElementById('album-detail-image');
|
||||
const titleEl = document.getElementById('album-detail-title');
|
||||
const metaEl = document.getElementById('album-detail-meta');
|
||||
const tracklistContainer = document.getElementById('album-detail-tracklist');
|
||||
tracklistContainer.innerHTML = createPlaceholder('Loading...', true);
|
||||
|
||||
imageEl.src = '';
|
||||
imageEl.style.backgroundColor = 'var(--muted)';
|
||||
titleEl.innerHTML = '<div class="skeleton" style="height: 48px; width: 300px; max-width: 90%;"></div>';
|
||||
metaEl.innerHTML = '<div class="skeleton" style="height: 16px; width: 200px; max-width: 80%;"></div>';
|
||||
tracklistContainer.innerHTML = `
|
||||
<div class="track-list-header">
|
||||
<span style="width: 40px; text-align: center;">#</span>
|
||||
<span>Title</span>
|
||||
<span class="duration-header">Duration</span>
|
||||
</div>
|
||||
${this.createSkeletonTracks(10, false)}
|
||||
`;
|
||||
|
||||
try {
|
||||
const { album, tracks } = await this.api.getAlbum(albumId);
|
||||
|
||||
document.getElementById('album-detail-image').src = this.api.getCoverUrl(album.cover);
|
||||
document.getElementById('album-detail-title').textContent = album.title;
|
||||
document.getElementById('album-detail-meta').innerHTML =
|
||||
imageEl.src = this.api.getCoverUrl(album.cover);
|
||||
imageEl.style.backgroundColor = '';
|
||||
titleEl.textContent = album.title;
|
||||
metaEl.innerHTML =
|
||||
`By <a href="#artist/${album.artist.id}">${album.artist.name}</a> • ${new Date(album.releaseDate).getFullYear()}`;
|
||||
|
||||
tracklistContainer.innerHTML = `
|
||||
|
|
@ -193,19 +257,27 @@ async renderSearchPage(query) {
|
|||
|
||||
async renderArtistPage(artistId) {
|
||||
this.showPage('artist');
|
||||
|
||||
const imageEl = document.getElementById('artist-detail-image');
|
||||
const nameEl = document.getElementById('artist-detail-name');
|
||||
const metaEl = document.getElementById('artist-detail-meta');
|
||||
const tracksContainer = document.getElementById('artist-detail-tracks');
|
||||
const albumsContainer = document.getElementById('artist-detail-albums');
|
||||
|
||||
tracksContainer.innerHTML = albumsContainer.innerHTML = createPlaceholder('Loading...', true);
|
||||
imageEl.src = '';
|
||||
imageEl.style.backgroundColor = 'var(--muted)';
|
||||
nameEl.innerHTML = '<div class="skeleton" style="height: 48px; width: 300px; max-width: 90%;"></div>';
|
||||
metaEl.innerHTML = '<div class="skeleton" style="height: 16px; width: 150px;"></div>';
|
||||
tracksContainer.innerHTML = this.createSkeletonTracks(5, true);
|
||||
albumsContainer.innerHTML = this.createSkeletonCards(6, false);
|
||||
|
||||
try {
|
||||
const artist = await this.api.getArtist(artistId);
|
||||
|
||||
document.getElementById('artist-detail-image').src =
|
||||
this.api.getArtistPictureUrl(artist.picture, '750');
|
||||
document.getElementById('artist-detail-name').textContent = artist.name;
|
||||
document.getElementById('artist-detail-meta').textContent =
|
||||
`${artist.popularity} popularity`;
|
||||
imageEl.src = this.api.getArtistPictureUrl(artist.picture, '750');
|
||||
imageEl.style.backgroundColor = '';
|
||||
nameEl.textContent = artist.name;
|
||||
metaEl.textContent = `${artist.popularity} popularity`;
|
||||
|
||||
this.renderListWithTracks(tracksContainer, artist.tracks, true);
|
||||
albumsContainer.innerHTML = artist.albums.map(album =>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
//js/utils.js
|
||||
export const QUALITY = 'LOSSLESS';
|
||||
|
||||
export const REPEAT_MODE = {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
"name": "Search",
|
||||
"short_name": "Search",
|
||||
"description": "Search for music",
|
||||
"url": "/#search",
|
||||
"url": "/search",
|
||||
"icons": [
|
||||
{
|
||||
"src": "https://prigoana.com/favicon.png",
|
||||
|
|
|
|||
354
styles.css
354
styles.css
|
|
@ -1,3 +1,4 @@
|
|||
/*styles.css*/
|
||||
:root {
|
||||
--background: #000;
|
||||
--foreground: #fafafa;
|
||||
|
|
@ -15,6 +16,12 @@
|
|||
--radius: .5rem;
|
||||
--highlight: #4ade80;
|
||||
--active-highlight: var(--highlight);
|
||||
--spacing-xs: 0.5rem;
|
||||
--spacing-sm: 0.75rem;
|
||||
--spacing-md: 1rem;
|
||||
--spacing-lg: 1.5rem;
|
||||
--spacing-xl: 2rem;
|
||||
--spacing-2xl: 3rem;
|
||||
}
|
||||
|
||||
*, *::before, *::after {
|
||||
|
|
@ -34,10 +41,13 @@ body {
|
|||
font-family: 'Inter', sans-serif;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
background-color: var(--muted);
|
||||
color: transparent;
|
||||
font-size: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
a {
|
||||
|
|
@ -70,18 +80,18 @@ a {
|
|||
.main-content {
|
||||
grid-area: main;
|
||||
overflow-y: auto;
|
||||
padding: 2rem;
|
||||
padding: var(--spacing-xl);
|
||||
}
|
||||
|
||||
.now-playing-bar {
|
||||
grid-area: player;
|
||||
background-color: #050505;
|
||||
border-top: 1px solid var(--border);
|
||||
padding: .75rem 1.5rem;
|
||||
padding: var(--spacing-md) var(--spacing-lg);
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2fr 1fr;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
gap: var(--spacing-xl);
|
||||
}
|
||||
|
||||
.sidebar-logo {
|
||||
|
|
@ -140,15 +150,22 @@ a {
|
|||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(8px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.main-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
margin-bottom: var(--spacing-xl);
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.hamburger-menu {
|
||||
|
|
@ -192,19 +209,19 @@ a {
|
|||
}
|
||||
|
||||
.content-section {
|
||||
margin-bottom: 3rem;
|
||||
margin-bottom: var(--spacing-2xl);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 1.5rem;
|
||||
margin-bottom: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.search-tabs {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
gap: var(--spacing-xs);
|
||||
margin-bottom: var(--spacing-lg);
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
|
|
@ -212,7 +229,7 @@ a {
|
|||
background: transparent;
|
||||
border: none;
|
||||
color: var(--muted-foreground);
|
||||
padding: 0.75rem 1.5rem;
|
||||
padding: var(--spacing-sm) var(--spacing-lg);
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
|
|
@ -241,7 +258,7 @@ a {
|
|||
.card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||||
gap: 1.5rem;
|
||||
gap: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.card {
|
||||
|
|
@ -288,19 +305,19 @@ a {
|
|||
.track-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: .25rem;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.track-list .track-list-header {
|
||||
display: grid;
|
||||
grid-template-columns: 40px 1fr auto;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: .75rem;
|
||||
gap: var(--spacing-md);
|
||||
padding: var(--spacing-sm) var(--spacing-sm);
|
||||
color: var(--muted-foreground);
|
||||
font-size: .9rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
margin-bottom: .5rem;
|
||||
margin-bottom: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.track-list .track-list-header .duration-header {
|
||||
|
|
@ -311,8 +328,8 @@ a {
|
|||
display: grid;
|
||||
grid-template-columns: 40px 1fr auto;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: .75rem;
|
||||
gap: var(--spacing-md);
|
||||
padding: var(--spacing-sm);
|
||||
border-radius: var(--radius);
|
||||
cursor: pointer;
|
||||
transition: background-color .2s ease-in-out;
|
||||
|
|
@ -331,7 +348,7 @@ a {
|
|||
.track-item-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
gap: var(--spacing-md);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
|
|
@ -377,8 +394,9 @@ a {
|
|||
.detail-header {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 2rem;
|
||||
margin-bottom: 3rem;
|
||||
gap: var(--spacing-xl);
|
||||
margin-bottom: var(--spacing-2xl);
|
||||
padding-bottom: var(--spacing-xl);
|
||||
}
|
||||
|
||||
.detail-header-image {
|
||||
|
|
@ -389,6 +407,11 @@ a {
|
|||
border-radius: var(--radius);
|
||||
object-fit: cover;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, .5);
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.detail-header-image.loading {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.detail-header-image.artist {
|
||||
|
|
@ -411,12 +434,17 @@ a {
|
|||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.settings-list {
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.setting-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem 0;
|
||||
padding: var(--spacing-lg) 0;
|
||||
border-bottom: 1px solid var(--border);
|
||||
gap: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.setting-item .info {
|
||||
|
|
@ -546,13 +574,13 @@ input:checked + .slider:before {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: .5rem;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.player-controls .buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.player-controls .buttons button {
|
||||
|
|
@ -977,16 +1005,186 @@ input:checked + .slider:before {
|
|||
border-left: 3px solid var(--muted-foreground);
|
||||
}
|
||||
|
||||
.skeleton {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
var(--secondary) 0%,
|
||||
var(--muted) 50%,
|
||||
var(--secondary) 100%
|
||||
);
|
||||
background-size: 200% 100%;
|
||||
animation: skeleton-loading 1.5s ease-in-out infinite;
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
@keyframes skeleton-loading {
|
||||
0% {
|
||||
background-position: 200% 0;
|
||||
}
|
||||
100% {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
}
|
||||
|
||||
.skeleton-track {
|
||||
display: grid;
|
||||
grid-template-columns: 40px 1fr auto;
|
||||
align-items: center;
|
||||
gap: var(--spacing-md);
|
||||
padding: var(--spacing-sm);
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.skeleton-track-number {
|
||||
width: 24px;
|
||||
height: 20px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.skeleton-track-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-md);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.skeleton-track-cover {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.skeleton-track-details {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.skeleton-track-title {
|
||||
height: 16px;
|
||||
width: 60%;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.skeleton-track-artist {
|
||||
height: 14px;
|
||||
width: 40%;
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
.skeleton-track-duration {
|
||||
width: 40px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.skeleton-card {
|
||||
background-color: var(--card);
|
||||
border-radius: var(--radius);
|
||||
padding: var(--spacing-md);
|
||||
}
|
||||
|
||||
.skeleton-card-image {
|
||||
width: 100%;
|
||||
aspect-ratio: 1/1;
|
||||
margin-bottom: var(--spacing-md);
|
||||
border-radius: calc(var(--radius) - 4px);
|
||||
}
|
||||
|
||||
.skeleton-card.artist .skeleton-card-image {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.skeleton-card-title {
|
||||
height: 18px;
|
||||
width: 80%;
|
||||
margin-bottom: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.skeleton-card-subtitle {
|
||||
height: 14px;
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.skeleton-detail-header {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: var(--spacing-xl);
|
||||
margin-bottom: var(--spacing-2xl);
|
||||
padding-bottom: var(--spacing-xl);
|
||||
}
|
||||
|
||||
.skeleton-detail-image {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
flex-shrink: 0;
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
.skeleton-detail-image.artist {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.skeleton-detail-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.skeleton-detail-type {
|
||||
height: 16px;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.skeleton-detail-title {
|
||||
height: 48px;
|
||||
width: 70%;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.skeleton-detail-meta {
|
||||
height: 16px;
|
||||
width: 50%;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.skeleton-container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content-loaded {
|
||||
animation: contentFadeIn 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes contentFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.app-container {
|
||||
grid-template-columns: 240px 1fr;
|
||||
}
|
||||
|
||||
.card-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.detail-header-info .title {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
padding: var(--spacing-lg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
|
|
@ -998,15 +1196,18 @@ input:checked + .slider:before {
|
|||
"main"
|
||||
"player";
|
||||
}
|
||||
|
||||
.main-content {
|
||||
padding: 1rem;
|
||||
padding: var(--spacing-md);
|
||||
grid-area: main;
|
||||
}
|
||||
|
||||
.main-header {
|
||||
grid-area: header;
|
||||
padding: 1rem 1rem 0 1rem;
|
||||
margin-bottom: 0;
|
||||
padding: var(--spacing-md) var(--spacing-md) 0 var(--spacing-md);
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
|
|
@ -1015,68 +1216,153 @@ input:checked + .slider:before {
|
|||
transform: translateX(-100%);
|
||||
box-shadow: 0 0 20px rgba(0, 0, 0, .5);
|
||||
}
|
||||
|
||||
.sidebar.is-open {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.hamburger-menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#sidebar-overlay.is-visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.content-section {
|
||||
margin-bottom: var(--spacing-xl);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
.card-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: var(--spacing-lg);
|
||||
padding-bottom: var(--spacing-md);
|
||||
margin-bottom: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.detail-header-image {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
.detail-header-info .title {
|
||||
font-size: 2.5rem;
|
||||
font-size: 2rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.skeleton-detail-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.skeleton-detail-image {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
.skeleton-detail-title {
|
||||
height: 40px;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.now-playing-bar {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: auto auto;
|
||||
gap: .75rem;
|
||||
padding: 1rem;
|
||||
gap: var(--spacing-md);
|
||||
padding: var(--spacing-md);
|
||||
height: auto;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.now-playing-bar .track-info {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.track-info .details {
|
||||
max-width: calc(100% - 72px);
|
||||
|
||||
.track-info .cover {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.track-info .details {
|
||||
max-width: calc(100% - 64px);
|
||||
}
|
||||
|
||||
.track-info .details .artist {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.now-playing-bar .player-controls {
|
||||
grid-column: 1;
|
||||
grid-row: 2;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
gap: .75rem;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.player-controls .progress-container {
|
||||
display: flex;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.now-playing-bar .volume-controls {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.about-links {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.github-link {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.setting-item {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.setting-item .info {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.card-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.detail-header-info .title {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
|
||||
.search-tab {
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
1
sw.js
1
sw.js
|
|
@ -1,3 +1,4 @@
|
|||
//sw.js
|
||||
const CACHE_NAME = 'monochrome-v1';
|
||||
const urlsToCache = [
|
||||
'/',
|
||||
|
|
|
|||
Loading…
Reference in a new issue