diff --git a/static/css/album/album.css b/static/css/album/album.css index 6f25986..4f389e3 100644 --- a/static/css/album/album.css +++ b/static/css/album/album.css @@ -95,54 +95,52 @@ body { /* Individual Track Styling */ .track { - display: flex; + display: grid; + grid-template-columns: 40px 1fr auto auto; align-items: center; - padding: 1rem; - background: #181818; - border-radius: 8px; - transition: background 0.3s ease; - flex-wrap: wrap; + padding: 0.75rem 1rem; + border-radius: var(--radius-sm); + background-color: var(--color-surface); + margin-bottom: 0.5rem; + transition: background-color 0.2s ease; } .track:hover { - background: #2a2a2a; + background-color: var(--color-surface-hover); } .track-number { - width: 30px; - font-size: 1rem; - font-weight: 500; - text-align: center; - margin-right: 1rem; - flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.9rem; + color: var(--color-text-secondary); + width: 24px; } .track-info { + padding: 0 1rem; flex: 1; - display: flex; - flex-direction: column; min-width: 0; - align-items: flex-start; } .track-name { - font-size: 1rem; - font-weight: bold; - word-wrap: break-word; + font-weight: 500; + margin-bottom: 0.25rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .track-artist { font-size: 0.9rem; - color: #b3b3b3; + color: var(--color-text-secondary); } .track-duration { - width: 60px; - text-align: right; + color: var(--color-text-tertiary); font-size: 0.9rem; - color: #b3b3b3; - margin-left: 1rem; - flex-shrink: 0; + margin-right: 1rem; } /* Loading and Error States */ @@ -296,15 +294,12 @@ body { } .track { - flex-direction: column; - align-items: flex-start; + grid-template-columns: 30px 1fr auto auto; + padding: 0.6rem 0.8rem; } - + .track-duration { - margin-left: 0; - margin-top: 0.5rem; - width: 100%; - text-align: left; + margin-right: 0.5rem; } } @@ -336,23 +331,19 @@ body { } .track { - padding: 0.8rem; - flex-direction: column; - align-items: center; - text-align: center; + grid-template-columns: 30px 1fr auto; } - - .track-number { - font-size: 0.9rem; - margin-right: 0; - margin-bottom: 0.5rem; + + .track-info { + padding: 0 0.5rem; } - - .track-duration { - margin-left: 0; - margin-top: 0.5rem; - width: 100%; - text-align: center; + + .track-name, .track-artist { + max-width: 200px; + } + + .section-title { + font-size: 1.25rem; } /* Ensure the actions container lays out buttons properly */ @@ -385,3 +376,31 @@ a:focus { .download-btn--circle::before { content: none; } + +/* Album page specific styles */ + +/* Add some context styles for the album copyright */ +.album-copyright { + font-size: 0.85rem; + color: var(--color-text-tertiary); + margin-top: 0.5rem; + font-style: italic; +} + +/* Section title styling */ +.section-title { + font-size: 1.5rem; + margin-bottom: 1rem; + position: relative; + padding-bottom: 0.5rem; +} + +.section-title:after { + content: ''; + position: absolute; + left: 0; + bottom: 0; + width: 50px; + height: 2px; + background-color: var(--color-primary); +} diff --git a/static/css/artist/artist.css b/static/css/artist/artist.css index 459b808..97887e7 100644 --- a/static/css/artist/artist.css +++ b/static/css/artist/artist.css @@ -72,72 +72,94 @@ body { padding-bottom: 0.5rem; } -/* Album Groups */ +/* Album groups layout */ +.album-groups { + display: flex; + flex-direction: column; + gap: 2rem; +} + +/* Album group section */ .album-group { - margin-bottom: 2rem; + margin-bottom: 1.5rem; } .album-group-header { display: flex; - justify-content: space-between; align-items: center; + justify-content: space-between; margin-bottom: 1rem; - padding: 0.5rem 0; - border-bottom: 1px solid #2a2a2a; } .album-group-header h3 { - font-size: 1.5rem; - margin: 0; - text-transform: capitalize; + font-size: 1.3rem; + position: relative; + padding-bottom: 0.5rem; } +.album-group-header h3::after { + content: ''; + position: absolute; + left: 0; + bottom: 0; + width: 40px; + height: 2px; + background-color: var(--color-primary); +} + +.group-download-btn { + padding: 0.4rem 0.8rem; + font-size: 0.9rem; +} + +/* Albums grid layout */ .albums-list { - display: flex; - flex-direction: column; - gap: 1rem; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 1.5rem; + margin-top: 1rem; } -/* Unified Album Card (Desktop & Mobile) */ +/* Album card styling */ .album-card { - background: #181818; - border-radius: 8px; + background-color: var(--color-surface); + border-radius: var(--radius-md); overflow: hidden; - box-shadow: 0 2px 4px rgba(0,0,0,0.2); transition: transform 0.2s ease, box-shadow 0.2s ease; - display: flex; - align-items: center; - padding: 0.5rem; + position: relative; + box-shadow: var(--shadow-sm); } .album-card:hover { transform: translateY(-5px); - box-shadow: 0 4px 8px rgba(0,0,0,0.3); + box-shadow: var(--shadow-md); } -/* Album Cover Image */ .album-cover { - width: 80px; - height: 80px; + width: 100%; + aspect-ratio: 1/1; object-fit: cover; - border-radius: 4px; - margin-right: 1rem; + transition: opacity 0.2s ease; } -/* Album Info */ .album-info { - flex: 1; + padding: 0.75rem; } .album-title { - font-size: 1rem; - font-weight: bold; + font-weight: 600; margin-bottom: 0.25rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .album-artist { font-size: 0.9rem; - color: #b3b3b3; + color: var(--color-text-secondary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } /* Track Card (for Albums or Songs) */ @@ -331,6 +353,20 @@ body { width: 100%; text-align: center; } + + .albums-list { + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + gap: 1rem; + } + + .album-group-header { + flex-direction: column; + align-items: flex-start; + } + + .group-download-btn { + margin-top: 0.5rem; + } } /* Small Devices (Mobile Phones) */ @@ -368,36 +404,21 @@ body { text-align: center; } - /* Mobile Album Grid Styles Inspired by Spotify */ .albums-list { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 1rem; - } - - /* Adjust album card for mobile grid */ - .album-card { - flex-direction: column; - padding: 0; - } - - .album-cover { - width: 100%; - aspect-ratio: 1 / 1; - margin: 0; + grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); + gap: 0.75rem; } .album-info { padding: 0.5rem; - text-align: center; } .album-title { - font-size: 1rem; + font-size: 0.9rem; } .album-artist { - font-size: 0.9rem; + font-size: 0.8rem; } } diff --git a/static/css/config/config.css b/static/css/config/config.css index afc5d17..5e1d15d 100644 --- a/static/css/config/config.css +++ b/static/css/config/config.css @@ -32,39 +32,223 @@ body { transition: all 0.3s ease; } -/* Modern Back Button */ -.back-button { - background: #1db954; - color: #ffffff; - padding: 0.8rem 1.5rem; - border-radius: 25px; - text-decoration: none; - font-weight: 500; - transition: background 0.3s ease, transform 0.2s ease; +/* Back button as floating icon - keep this for our floating button */ +.back-button.floating-icon { + position: fixed; + width: 56px; + height: 56px; + bottom: 20px; + left: 20px; + background-color: var(--color-primary); + border-radius: 50%; + box-shadow: var(--shadow-lg); + z-index: 9999; + display: flex; + align-items: center; + justify-content: center; + transition: transform 0.2s ease, background-color 0.2s ease; + text-decoration: none !important; } -.back-button:hover { - background: #1ed760; +.back-button.floating-icon:hover { + background-color: var(--color-primary-hover); + transform: scale(1.05); +} + +.back-button.floating-icon img { + width: 24px; + height: 24px; + filter: brightness(0) invert(1); + margin: 0; +} + +/* Queue button as floating icon */ +.queue-icon.floating-icon { + position: fixed; + width: 56px; + height: 56px; + bottom: 20px; + right: 20px; + background-color: var(--color-primary); + border-radius: 50%; + box-shadow: var(--shadow-lg); + z-index: 9999; + display: flex; + align-items: center; + justify-content: center; + transition: transform 0.2s ease, background-color 0.2s ease; + text-decoration: none !important; +} + +.queue-icon.floating-icon:hover { + background-color: var(--color-primary-hover); + transform: scale(1.05); +} + +.queue-icon.floating-icon img { + width: 24px; + height: 24px; + filter: brightness(0) invert(1); + margin: 0; +} + +/* Queue Icon Active State */ +.queue-icon.queue-icon-active { + background-color: #d13838 !important; + transition: background-color 0.3s ease; +} + +.queue-icon.queue-icon-active:hover { + background-color: #e04c4c !important; +} + +.queue-icon .queue-x { + font-size: 28px; + font-weight: bold; + color: white; + line-height: 24px; + display: inline-block; transform: translateY(-2px); } -/* Queue Icon in Header */ -#queueIcon { - background: none; +/* Queue Sidebar for Config Page */ +#downloadQueue { + position: fixed; + top: 0; + right: -350px; + width: 350px; + height: 100vh; + background: #181818; + padding: 20px; + transition: right 0.3s cubic-bezier(0.4, 0, 0.2, 1); + z-index: 1001; + box-shadow: -20px 0 30px rgba(0, 0, 0, 0.4); + display: flex; + flex-direction: column; +} + +#downloadQueue.active { + right: 0; +} + +/* Header inside the queue sidebar */ +.sidebar-header { + display: flex; + justify-content: space-between; + align-items: center; + padding-bottom: 15px; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + margin-bottom: 20px; +} + +.sidebar-header h2 { + font-size: 1.25rem; + font-weight: 600; + color: #fff; + margin: 0; +} + +.header-actions { + display: flex; + gap: 10px; + align-items: center; +} + +/* Cancel all button styling */ +#cancelAllBtn { + background: #8b0000; border: none; + color: #fff; + padding: 8px 12px; + border-radius: 4px; cursor: pointer; - padding: 4px; + transition: background 0.3s ease, transform 0.2s ease; + font-size: 14px; + font-weight: 600; + display: flex; + align-items: center; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5); } -#queueIcon img { - width: 24px; - height: 24px; - filter: invert(1); - transition: opacity 0.3s ease; +#cancelAllBtn:hover { + background: #a30000; + transform: translateY(-1px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4); } -#queueIcon:hover img { - opacity: 0.8; +#cancelAllBtn:active { + transform: scale(0.98); +} + +#cancelAllBtn .skull-icon { + width: 16px; + height: 16px; + margin-right: 8px; + vertical-align: middle; + filter: brightness(0) invert(1); + transition: transform 0.3s ease; +} + +#cancelAllBtn:hover .skull-icon { + transform: rotate(-10deg) scale(1.2); + animation: skullShake 0.5s infinite alternate; +} + +@keyframes skullShake { + 0% { transform: rotate(-5deg); } + 100% { transform: rotate(5deg); } +} + +/* Empty queue state */ +.queue-empty { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 200px; + color: #b3b3b3; + text-align: center; + padding: 20px; +} + +.queue-empty img { + width: 60px; + height: 60px; + margin-bottom: 15px; + opacity: 0.6; +} + +.queue-empty p { + font-size: 14px; + line-height: 1.5; +} + +/* Mobile responsiveness for queue in Config page */ +@media (max-width: 600px) { + #downloadQueue { + width: 100%; + right: -100%; + padding: 15px; + } + + #downloadQueue.active { + right: 0; + } + + .sidebar-header { + padding-bottom: 12px; + margin-bottom: 15px; + } + + .sidebar-header h2 { + font-size: 1.1rem; + } + + #cancelAllBtn { + padding: 6px 10px; + font-size: 12px; + } } /* Account Configuration Section */ @@ -81,6 +265,25 @@ body { transform: translateY(-2px); } +/* Section Titles */ +.section-title { + font-size: 1.5rem; + margin-bottom: 1.5rem; + position: relative; + padding-bottom: 0.75rem; + color: var(--color-text-primary); +} + +.section-title::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 50px; + height: 2px; + background-color: var(--color-primary); +} + .config-item { margin-bottom: 1.5rem; position: relative; @@ -94,10 +297,7 @@ body { } /* Enhanced Dropdown Styling */ -#spotifyAccountSelect, -#deezerAccountSelect, -#spotifyQualitySelect, -#deezerQualitySelect { +.form-select { background: #2a2a2a; color: #ffffff; border: 1px solid #404040; @@ -115,33 +315,24 @@ body { transition: all 0.3s ease; } -#spotifyAccountSelect:focus, -#deezerAccountSelect:focus, -#spotifyQualitySelect:focus, -#deezerQualitySelect:focus { +.form-select:focus { outline: none; border-color: #1db954; box-shadow: 0 0 0 2px rgba(29, 185, 84, 0.2); } -#spotifyAccountSelect option, -#deezerAccountSelect option, -#spotifyQualitySelect option, -#deezerQualitySelect option { +.form-select option { background: #181818; color: #ffffff; padding: 0.8rem; } -#spotifyAccountSelect option:hover, -#deezerAccountSelect option:hover, -#spotifyQualitySelect option:hover, -#deezerQualitySelect option:hover { +.form-select option:hover { background: #1db954; } /* New Input Styling for Custom Format Fields */ -.config-item input[type="text"] { +.form-input { width: 100%; padding: 0.8rem; background: #2a2a2a; @@ -152,22 +343,29 @@ body { transition: border-color 0.3s ease; } -.config-item input[type="text"]:focus { +.form-input:focus { outline: none; border-color: #1db954; + box-shadow: 0 0 0 2px rgba(29, 185, 84, 0.2); } /* Improved Toggle Switches */ .switch { position: relative; display: inline-block; - width: 40px; - height: 20px; - margin-left: 1rem; + width: 48px; + height: 24px; + margin-top: 0.5rem; vertical-align: middle; overflow: hidden; } +.switch input { + opacity: 0; + width: 0; + height: 0; +} + .slider { position: absolute; cursor: pointer; @@ -177,16 +375,16 @@ body { bottom: 0; background-color: #666; transition: 0.4s; - border-radius: 20px; + border-radius: 24px; } .slider:before { position: absolute; content: ""; - height: 16px; - width: 16px; - left: 2px; - bottom: 2px; + height: 18px; + width: 18px; + left: 3px; + bottom: 3px; background-color: #ffffff; transition: 0.4s; border-radius: 50%; @@ -198,7 +396,7 @@ input:checked + .slider { } input:checked + .slider:before { - transform: translateX(20px); + transform: translateX(24px); } /* Setting description */ @@ -209,6 +407,11 @@ input:checked + .slider:before { line-height: 1.4; } +/* Accounts section layout */ +.accounts-section { + margin-top: 2rem; +} + /* Service Tabs */ .service-tabs { display: flex; @@ -225,6 +428,8 @@ input:checked + .slider:before { cursor: pointer; font-size: 0.95rem; transition: background 0.3s ease, transform 0.2s ease; + flex: 1; + text-align: center; } .tab-button.active { @@ -232,6 +437,10 @@ input:checked + .slider:before { transform: translateY(-2px); } +.tab-button:hover:not(.active) { + background: #333333; +} + /* No Credentials Message */ .no-credentials { padding: 1.5rem; @@ -245,21 +454,32 @@ input:checked + .slider:before { /* Credentials List */ .credentials-list { margin-bottom: 2rem; + padding: 1.5rem; + background: #181818; + border-radius: 12px; + box-shadow: 0 4px 8px rgba(0,0,0,0.15); } .credential-item { display: flex; justify-content: space-between; align-items: center; - padding: 1rem; + padding: 1.25rem; background: #2a2a2a; border-radius: 8px; margin-bottom: 0.75rem; - transition: background 0.3s ease; + transition: all 0.3s ease; + border-left: 3px solid var(--color-primary); } .credential-item:hover { - background: #3a3a3a; + background: #333333; + transform: translateY(-2px); + box-shadow: var(--shadow-md); +} + +.credential-item:last-child { + margin-bottom: 0; } /* New styling for credential info and actions */ @@ -267,19 +487,40 @@ input:checked + .slider:before { display: flex; flex-direction: column; gap: 0.25rem; + flex: 1; + padding-right: 1rem; } .credential-name { font-weight: 600; - font-size: 1rem; + font-size: 1.1rem; + color: var(--color-text-primary); + margin-bottom: 0.5rem; +} + +.credential-type { + display: inline-block; + padding: 0.25rem 0.5rem; + background-color: rgba(29, 185, 84, 0.1); + border-radius: 8px; + font-size: 0.8rem; + color: var(--color-primary); + margin-bottom: 0.5rem; +} + +.credential-details { + font-size: 0.9rem; + color: var(--color-text-secondary); + margin-top: 0.25rem; } .search-credentials-status { - font-size: 0.8rem; - padding: 0.2rem 0.5rem; + font-size: 0.85rem; + padding: 0.25rem 0.5rem; border-radius: 12px; display: inline-block; width: fit-content; + margin-top: 0.5rem; } .search-credentials-status.has-api { @@ -294,37 +535,44 @@ input:checked + .slider:before { .credential-actions { display: flex; - gap: 0.5rem; + gap: 0.75rem; } .credential-actions button { - padding: 0.5rem 0.8rem; + background-color: #222222; border: none; - border-radius: 6px; cursor: pointer; - font-size: 0.8rem; - transition: opacity 0.3s ease, transform 0.2s ease; - white-space: nowrap; + padding: 0.6rem; + border-radius: 8px; + transition: background-color 0.2s ease, transform 0.2s ease; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.9rem; + font-weight: 500; + color: white; } -.edit-btn { - background: #1db954; - color: #ffffff; -} - -.edit-search-btn { - background: #2d6db5; - color: #ffffff; -} - -.delete-btn { - background: #ff5555; - color: #ffffff; +.credential-actions button img { + width: 20px; + height: 20px; + filter: brightness(0) invert(1); } .credential-actions button:hover { - opacity: 0.9; - transform: translateY(-1px); + transform: translateY(-2px); +} + +.credential-actions button.delete-btn { + color: #ff5555; +} + +.credential-actions button.delete-btn:hover { + background-color: rgba(192, 57, 43, 0.2); +} + +.credential-actions button.edit-btn:hover { + background-color: rgba(52, 152, 219, 0.2); } /* Credentials Form */ @@ -375,10 +623,10 @@ input:checked + .slider:before { padding: 0.8rem 1.5rem; border: none; border-radius: 25px; - width: 100%; cursor: pointer; font-weight: 500; transition: background 0.3s ease, transform 0.2s ease; + margin-top: 1.5rem; } .save-btn:hover { @@ -386,23 +634,39 @@ input:checked + .slider:before { transform: translateY(-2px); } -/* Error Messages */ +/* Error Messages - Hidden by default */ #configError { + background-color: rgba(192, 57, 43, 0.1); color: #ff5555; - margin-top: 1rem; - text-align: center; + margin-top: 1.5rem; + padding: 1rem; + border-radius: 8px; + border-left: 3px solid #ff5555; font-size: 0.9rem; - min-height: 1.2rem; + display: none; } -/* Success Messages */ +/* Show the messages when they have content */ +#configError:not(:empty) { + display: block; +} + +/* Success Messages - Hidden by default */ #configSuccess { + background-color: rgba(46, 204, 113, 0.1); color: #1db954; - margin-top: 1rem; - text-align: center; + margin-top: 1.5rem; + padding: 1rem; + border-radius: 8px; + border-left: 3px solid #1db954; font-size: 0.9rem; - min-height: 1.2rem; font-weight: 500; + display: none; +} + +/* Show the messages when they have content */ +#configSuccess:not(:empty) { + display: block; } /* MOBILE RESPONSIVENESS */ @@ -418,16 +682,7 @@ input:checked + .slider:before { } /* Increase touch target sizes for buttons and selects */ - .back-button { - width: 100%; - text-align: center; - padding: 0.8rem; - } - - #spotifyAccountSelect, - #deezerAccountSelect, - #spotifyQualitySelect, - #deezerQualitySelect { + .form-select { padding: 0.8rem 2rem 0.8rem 1rem; font-size: 0.9rem; } @@ -448,31 +703,28 @@ input:checked + .slider:before { gap: 0.75rem; } + .credential-info { + width: 100%; + margin-bottom: 1rem; + } + .credential-actions { width: 100%; display: flex; - justify-content: space-between; + justify-content: flex-end; flex-wrap: wrap; gap: 0.5rem; } - .credential-actions button { - flex: 1; - text-align: center; - padding: 0.7rem 0.5rem; - } - /* Adjust toggle switch size for better touch support */ .switch { - width: 50px; - height: 24px; + width: 52px; + height: 26px; } .slider:before { - height: 18px; - width: 18px; - left: 3px; - bottom: 3px; + height: 20px; + width: 20px; } } @@ -482,25 +734,60 @@ input:checked + .slider:before { } .account-config, + .credentials-list, .credentials-form { padding: 1rem; + border-radius: 8px; } - .form-group input { - padding: 0.7rem; + .section-title { + font-size: 1.3rem; } - .save-btn, - .back-button { + .config-item label { + font-size: 0.95rem; + } + + .form-select, + .form-input { + padding: 0.7rem 1.8rem 0.7rem 0.8rem; + font-size: 0.9rem; + } + + .save-btn { + width: 100%; padding: 0.7rem; font-size: 0.9rem; } - /* Reduce dropdown padding for very small screens */ - #spotifyAccountSelect, - #deezerAccountSelect, - #spotifyQualitySelect, - #deezerQualitySelect { - padding: 0.7rem 1.8rem 0.7rem 0.8rem; + /* Position floating icons a bit closer to the edges on small screens */ + .back-button.floating-icon { + width: 60px; + height: 60px; + left: 16px; + bottom: 16px; + } + + .back-button.floating-icon img { + width: 28px; + height: 28px; + } + + /* Queue icon mobile styles */ + .queue-icon.floating-icon { + width: 50px; + height: 50px; + right: 16px; + bottom: 16px; + } + + .queue-icon.floating-icon img { + width: 22px; + height: 22px; + } + + .queue-icon .queue-x { + font-size: 24px; + line-height: 20px; } } diff --git a/static/css/main/base.css b/static/css/main/base.css new file mode 100644 index 0000000..1f71df9 --- /dev/null +++ b/static/css/main/base.css @@ -0,0 +1,457 @@ +/* Spotizerr Base Styles + Provides consistent styling across all pages */ + +/* Reset and base styles */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +:root { + /* Main colors */ + --color-background: #121212; + --color-background-gradient: linear-gradient(135deg, #121212, #1e1e1e); + --color-surface: #1c1c1c; + --color-surface-hover: #2a2a2a; + --color-border: #2a2a2a; + + /* Text colors */ + --color-text-primary: #ffffff; + --color-text-secondary: #b3b3b3; + --color-text-tertiary: #757575; + + /* Brand colors */ + --color-primary: #1db954; + --color-primary-hover: #17a44b; + --color-error: #c0392b; + --color-success: #2ecc71; + + /* Spacing */ + --space-xs: 0.25rem; + --space-sm: 0.5rem; + --space-md: 1rem; + --space-lg: 1.5rem; + --space-xl: 2rem; + + /* Shadow */ + --shadow-sm: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); + --shadow-md: 0 4px 6px rgba(0,0,0,0.1); + --shadow-lg: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23); + + /* Border radius */ + --radius-sm: 4px; + --radius-md: 8px; + --radius-lg: 12px; + --radius-round: 50%; +} + +body { + background: var(--color-background-gradient); + color: var(--color-text-primary); + min-height: 100vh; + line-height: 1.4; +} + +a { + color: inherit; + text-decoration: none; + transition: color 0.2s ease; +} + +a:hover, a:focus { + color: var(--color-primary); + text-decoration: underline; +} + +/* Container for main content */ +.app-container { + max-width: 1200px; + margin: 0 auto; + padding: var(--space-lg); + position: relative; + z-index: 1; +} + +/* Card component */ +.card { + background: var(--color-surface); + border-radius: var(--radius-md); + padding: var(--space-md); + box-shadow: var(--shadow-sm); + transition: transform 0.2s ease, box-shadow 0.2s ease; +} + +.card:hover { + transform: translateY(-2px); + box-shadow: var(--shadow-md); +} + +/* Button variants */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + padding: var(--space-sm) var(--space-md); + border: none; + border-radius: var(--radius-sm); + cursor: pointer; + font-weight: 500; + transition: background-color 0.2s ease, transform 0.2s ease; + background-color: var(--color-surface-hover); + color: var(--color-text-primary); +} + +.btn:hover { + transform: translateY(-1px); +} + +.btn:active { + transform: scale(0.98); +} + +.btn-primary { + background-color: var(--color-primary); + color: white; +} + +.btn-primary:hover { + background-color: var(--color-primary-hover); +} + +/* Icon button */ +.btn-icon { + width: 40px; + height: 40px; + border-radius: var(--radius-round); + padding: 0; + display: inline-flex; + align-items: center; + justify-content: center; + background: transparent; + border: none; + cursor: pointer; + transition: background-color 0.2s ease; +} + +.btn-icon:hover { + background-color: var(--color-surface-hover); +} + +.btn-icon img { + width: 20px; + height: 20px; + filter: brightness(0) invert(1); +} + +/* Queue icon styling */ +.queue-icon { + background-color: transparent; + transition: background-color 0.2s ease, transform 0.2s ease; +} + +.queue-icon:hover { + background-color: var(--color-surface-hover); +} + +/* Floating icons (queue and settings) */ +.floating-icon { + position: fixed; + width: 56px; + height: 56px; + bottom: 20px; + background-color: var(--color-primary); + border-radius: var(--radius-round); + box-shadow: var(--shadow-lg); + z-index: 9999; + display: flex; + align-items: center; + justify-content: center; + transition: transform 0.2s ease, background-color 0.2s ease; + text-decoration: none !important; +} + +.floating-icon:hover, +.floating-icon:active { + background-color: var(--color-primary-hover); + transform: scale(1.05); +} + +.floating-icon img { + width: 24px; + height: 24px; + filter: brightness(0) invert(1); + margin: 0; +} + +/* Settings icon - bottom left */ +.settings-icon { + left: 20px; +} + +/* Queue icon - bottom right */ +.queue-icon { + right: 20px; +} + +/* Home button */ +.home-btn { + background-color: transparent; + border: none; + cursor: pointer; + padding: 0; +} + +.home-btn img { + width: 32px; + height: 32px; + filter: invert(1); + transition: transform 0.2s ease; +} + +.home-btn:hover img { + transform: scale(1.05); +} + +.home-btn:active img { + transform: scale(0.98); +} + +/* When home button is used as a floating button */ +.floating-icon.home-btn { + background-color: var(--color-primary); + padding: 0; + display: flex; + align-items: center; + justify-content: center; +} + +.floating-icon.home-btn img { + width: 24px; + height: 24px; + filter: brightness(0) invert(1); + margin: 0; +} + +/* Download button */ +.download-btn { + background-color: var(--color-primary); + border: none; + border-radius: var(--radius-sm); + padding: 0.7rem 1.2rem; + cursor: pointer; + font-weight: 600; + transition: background-color 0.2s ease, transform 0.2s ease; + display: inline-flex; + align-items: center; + justify-content: center; + color: white; +} + +.download-btn img { + width: 18px; + height: 18px; + margin-right: 8px; + filter: brightness(0) invert(1); +} + +.download-btn:hover { + background-color: var(--color-primary-hover); + transform: translateY(-2px); +} + +.download-btn:active { + transform: scale(0.98); +} + +.download-btn--circle { + width: 40px; + height: 40px; + border-radius: var(--radius-round); + padding: 0; +} + +.download-btn--circle img { + margin-right: 0; +} + +/* Header patterns */ +.content-header { + display: flex; + align-items: center; + gap: var(--space-md); + margin-bottom: var(--space-xl); + padding-bottom: var(--space-md); + border-bottom: 1px solid var(--color-border); +} + +.header-image { + width: 180px; + height: 180px; + object-fit: cover; + border-radius: var(--radius-md); + box-shadow: var(--shadow-md); +} + +.header-info { + flex: 1; +} + +.header-title { + font-size: 2rem; + margin-bottom: var(--space-sm); + font-weight: 700; +} + +.header-subtitle { + font-size: 1.1rem; + color: var(--color-text-secondary); + margin-bottom: var(--space-xs); +} + +.header-actions { + display: flex; + gap: var(--space-sm); + margin-top: var(--space-md); +} + +/* Track list styling */ +.tracks-list { + display: flex; + flex-direction: column; + gap: var(--space-xs); + margin-top: var(--space-md); +} + +.track-item { + display: grid; + grid-template-columns: 40px 1fr auto auto; + align-items: center; + padding: var(--space-sm) var(--space-md); + border-radius: var(--radius-sm); + transition: background-color 0.2s ease; +} + +.track-item:hover { + background-color: var(--color-surface-hover); +} + +/* Utility classes */ +.text-truncate { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.hidden { + display: none !important; +} + +.flex { + display: flex; +} + +.flex-column { + flex-direction: column; +} + +.items-center { + align-items: center; +} + +.justify-between { + justify-content: space-between; +} + +.gap-sm { + gap: var(--space-sm); +} + +.gap-md { + gap: var(--space-md); +} + +/* Loading and error states */ +.loading, +.error { + width: 100%; + text-align: center; + font-size: 1rem; + padding: var(--space-md); +} + +.error { + color: var(--color-error); +} + +/* Mobile responsiveness */ +@media (max-width: 768px) { + .content-header { + flex-direction: column; + text-align: center; + } + + .header-image { + width: 150px; + height: 150px; + } + + .header-title { + font-size: 1.75rem; + } + + .track-item { + grid-template-columns: 30px 1fr auto; + } +} + +@media (max-width: 480px) { + .app-container { + padding: var(--space-md); + } + + .header-image { + width: 120px; + height: 120px; + } + + .header-title { + font-size: 1.5rem; + } + + .header-subtitle { + font-size: 0.9rem; + } + + .header-actions { + flex-direction: column; + width: 100%; + } + + .download-btn { + width: 100%; + } + + /* Adjust floating icons size for very small screens */ + .floating-icon { + width: 60px; + height: 60px; + } + + .floating-icon img { + width: 28px; + height: 28px; + } + + /* Position floating icons a bit closer to the edges on small screens */ + .settings-icon { + left: 16px; + bottom: 16px; + } + + .queue-icon { + right: 16px; + bottom: 16px; + } +} \ No newline at end of file diff --git a/static/css/main/icons.css b/static/css/main/icons.css index bd34314..adac5cf 100644 --- a/static/css/main/icons.css +++ b/static/css/main/icons.css @@ -20,6 +20,46 @@ padding: 4px; } +/* Style for the skull icon in the Cancel all button */ +.skull-icon { + width: 16px; + height: 16px; + margin-right: 8px; + vertical-align: middle; + filter: brightness(0) invert(1); /* Makes icon white */ + transition: transform 0.3s ease; +} + +#cancelAllBtn:hover .skull-icon { + transform: rotate(-10deg) scale(1.2); + animation: skullShake 0.5s infinite alternate; +} + +@keyframes skullShake { + 0% { transform: rotate(-5deg); } + 100% { transform: rotate(5deg); } +} + +/* Style for the X that appears when the queue is visible */ +.queue-x { + font-size: 28px; + font-weight: bold; + color: white; + line-height: 24px; + display: inline-block; + transform: translateY(-2px); +} + +/* Queue icon with red tint when X is active */ +.queue-icon-active { + background-color: #d13838 !important; /* Red background for active state */ + transition: background-color 0.3s ease; +} + +.queue-icon-active:hover { + background-color: #e04c4c !important; /* Lighter red on hover */ +} + .download-icon, .type-icon, .toggle-chevron { diff --git a/static/css/main/main.css b/static/css/main/main.css index 8dbdc9c..5c069c2 100644 --- a/static/css/main/main.css +++ b/static/css/main/main.css @@ -24,18 +24,35 @@ body { /* LOADING & ERROR STATES */ .loading, -.error { +.error, +.success { width: 100%; text-align: center; font-size: 1rem; padding: 1rem; + position: fixed; + bottom: 20px; + left: 50%; + transform: translateX(-50%); + z-index: 9999; + border-radius: 8px; + max-width: 80%; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); } .error { - color: #c0392b; + color: #fff; + background-color: rgba(192, 57, 43, 0.9); } -/* SEARCH HEADER COMPONENT */ +.success { + color: #fff; + background-color: rgba(46, 204, 113, 0.9); +} + +/* Main search page specific styles */ + +/* Search header improvements */ .search-header { display: flex; align-items: center; @@ -43,11 +60,17 @@ body { margin-bottom: 30px; position: sticky; top: 0; - background: rgba(18, 18, 18, 1); + background: rgba(18, 18, 18, 0.95); backdrop-filter: blur(10px); padding: 20px 0; z-index: 100; - border-bottom: 1px solid #2a2a2a; + border-bottom: 1px solid var(--color-border); +} + +.search-input-container { + display: flex; + flex: 1; + gap: 10px; } .search-input { @@ -55,72 +78,121 @@ body { padding: 12px 20px; border: none; border-radius: 25px; - background: #2a2a2a; - color: #ffffff; + background: var(--color-surface); + color: var(--color-text-primary); font-size: 16px; outline: none; - transition: background-color 0.3s ease; + transition: background-color 0.3s ease, box-shadow 0.3s ease; } .search-input:focus { - background: #333333; + background: var(--color-surface-hover); + box-shadow: 0 0 0 2px rgba(29, 185, 84, 0.3); } .search-type { padding: 12px 15px; - background: #2a2a2a; + background: var(--color-surface); border: none; border-radius: 25px; - color: #ffffff; + color: var(--color-text-primary); cursor: pointer; transition: background-color 0.3s ease; + min-width: 100px; } -.search-type:hover { - background: #3a3a3a; +.search-type:hover, +.search-type:focus { + background: var(--color-surface-hover); } .search-button { - padding: 12px 30px; - background-color: #1db954; + padding: 12px 25px; + background-color: var(--color-primary); border: none; border-radius: 25px; - color: #ffffff; - font-weight: bold; + color: white; + font-weight: 600; cursor: pointer; transition: background-color 0.3s ease, transform 0.2s ease; + display: flex; + align-items: center; + gap: 8px; +} + +.search-button img { + width: 18px; + height: 18px; + filter: brightness(0) invert(1); } .search-button:hover { - background-color: #1ed760; + background-color: var(--color-primary-hover); transform: translateY(-2px); } -/* RESULTS GRID COMPONENT – Minimalistic Version */ +/* Empty state styles */ +.empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 60vh; + text-align: center; +} + +.empty-state-content { + max-width: 450px; +} + +.empty-state-icon { + width: 80px; + height: 80px; + margin-bottom: 1.5rem; + opacity: 0.7; +} + +.empty-state h2 { + font-size: 1.75rem; + margin-bottom: 1rem; + background: linear-gradient(90deg, var(--color-primary), #2ecc71); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.empty-state p { + color: var(--color-text-secondary); + font-size: 1rem; + line-height: 1.5; +} + +/* Results grid improvement */ .results-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); - gap: 15px; + gap: 20px; + margin-top: 20px; } -/* Each result card now features a clean, flat design with minimal decoration */ +/* Result card style */ .result-card { - background: #1c1c1c; /* A uniform dark background */ - border: 1px solid #2a2a2a; /* A subtle border for separation */ - border-radius: 4px; /* Slight rounding for a modern look */ + background: var(--color-surface); + border-radius: var(--radius-md); overflow: hidden; display: flex; flex-direction: column; - cursor: pointer; - transition: background-color 0.2s ease, transform 0.2s ease; + transition: transform 0.2s ease, box-shadow 0.2s ease; + box-shadow: var(--shadow-sm); + height: 100%; } .result-card:hover { - background-color: #2a2a2a; /* Lightens the card on hover */ - transform: translateY(-2px); /* A gentle lift effect */ + transform: translateY(-5px); + box-shadow: var(--shadow-md); } -/* Album/Art image wrapper – Maintains aspect ratio and a clean presentation */ +/* Album art styling */ .album-art-wrapper { position: relative; width: 100%; @@ -130,7 +202,7 @@ body { .album-art-wrapper::before { content: ""; display: block; - padding-top: 100%; /* 1:1 aspect ratio */ + padding-top: 100%; } .album-art { @@ -140,15 +212,19 @@ body { width: 100%; height: 100%; object-fit: cover; - transition: opacity 0.2s ease; + transition: transform 0.3s ease; } -/* Text details are kept simple and legible */ +.result-card:hover .album-art { + transform: scale(1.05); +} + +/* Track title and details */ .track-title { - padding: 0.75rem 1rem; + padding: 1rem 1rem 0.5rem; font-size: 1rem; - font-weight: bold; - color: #ffffff; + font-weight: 600; + color: var(--color-text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -157,128 +233,106 @@ body { .track-artist { padding: 0 1rem; font-size: 0.9rem; - color: #aaaaaa; - margin-top: 0.25rem; + color: var(--color-text-secondary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-bottom: 0.75rem; } .track-details { padding: 0.75rem 1rem; font-size: 0.85rem; - color: #bbbbbb; - border-top: 1px solid #2a2a2a; + color: var(--color-text-tertiary); + border-top: 1px solid var(--color-border); display: flex; justify-content: space-between; align-items: center; + margin-top: auto; } -.duration { - font-style: italic; - color: #999; -} - -/* Centered Download Button styling */ +/* Download button within result cards */ .download-btn { - margin: 0.75rem 1rem 1rem; - padding: 0.5rem 1rem; - background-color: #1db954; - color: #ffffff; - border: none; - border-radius: 4px; - font-size: 0.95rem; - cursor: pointer; - transition: background-color 0.2s ease, transform 0.2s ease; - display: block; - text-align: center; - width: calc(100% - 2rem); + margin: 0 1rem 1rem; + max-width: calc(100% - 2rem); /* Ensure button doesn't overflow container */ + width: auto; /* Allow button to shrink if needed */ + font-size: 0.9rem; /* Slightly smaller font size */ + padding: 0.6rem 1rem; /* Reduce padding slightly */ + overflow: hidden; /* Hide overflow */ + text-overflow: ellipsis; /* Add ellipsis for long text */ + white-space: nowrap; /* Prevent wrapping */ } -.download-btn:hover { - background-color: #17a44b; - transform: scale(1.02); -} - -/* ARTIST DOWNLOAD OPTIONS */ -.artist-download-buttons { - border-top: 1px solid #2a2a2a; - padding: 0.5rem 1rem; -} - -.options-toggle { - width: 100%; - background: none; - border: none; - color: #b3b3b3; - padding: 8px 16px; - display: flex; - justify-content: space-between; - align-items: center; - cursor: pointer; - transition: color 0.2s ease; -} - -.options-toggle:hover { - color: #ffffff; -} - -.download-options-container { - margin-top: 0.5rem; -} - -.secondary-options { - display: none; - flex-wrap: wrap; - gap: 0.5rem; - margin-top: 0.5rem; -} - -.secondary-options.expanded { - display: flex; -} - -.option-btn { - flex: 1; - background-color: #2a2a2a; - color: #ffffff; - padding: 0.4rem 0.6rem; - border: none; - border-radius: 4px; - font-size: 0.85rem; - cursor: pointer; - transition: background-color 0.2s ease, transform 0.2s ease; -} - -.option-btn:hover { - background-color: #3a3a3a; - transform: translateY(-1px); -} - -/* MOBILE RESPONSIVENESS */ -@media (max-width: 600px) { +/* Mobile responsiveness */ +@media (max-width: 768px) { .search-header { flex-wrap: wrap; - justify-content: center; - padding: 10px 0; + padding: 15px 0; + gap: 12px; } - .search-input, - .search-type, - .search-button { + .search-input-container { flex: 1 1 100%; - margin-bottom: 10px; + order: 1; } - .search-type, .search-button { - padding: 10px; - font-size: 15px; + order: 2; + flex: 1; } .results-grid { - justify-content: center; + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + gap: 15px; } - .result-card { - width: 90%; - margin: 0 auto; + /* Smaller download button for mobile */ + .download-btn { + padding: 0.5rem 0.8rem; + font-size: 0.85rem; + } +} + +@media (max-width: 480px) { + .search-header { + padding: 10px 0; + } + + .search-type { + min-width: 80px; + padding: 12px 10px; + } + + .search-button { + padding: 12px 15px; + } + + .results-grid { + grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); + gap: 12px; + } + + .track-title, .track-artist { + font-size: 0.9rem; + } + + .track-details { + font-size: 0.8rem; + } + + /* Even smaller download button for very small screens */ + .download-btn { + padding: 0.4rem 0.7rem; + font-size: 0.8rem; + margin: 0 0.8rem 0.8rem; + max-width: calc(100% - 1.6rem); + } + + .empty-state h2 { + font-size: 1.5rem; + } + + .empty-state p { + font-size: 0.9rem; } } diff --git a/static/css/playlist/playlist.css b/static/css/playlist/playlist.css index 500b65b..c684386 100644 --- a/static/css/playlist/playlist.css +++ b/static/css/playlist/playlist.css @@ -392,3 +392,70 @@ a:focus { .download-btn--circle:active { transform: scale(0.98); } + +/* Playlist page specific styles */ + +/* Playlist description */ +.playlist-description { + font-size: 0.9rem; + color: var(--color-text-secondary); + margin-top: 0.75rem; + max-width: 90%; + line-height: 1.5; +} + +/* Additional column for album in playlist tracks */ +.track-album { + font-size: 0.9rem; + color: var(--color-text-secondary); + margin-right: 1rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 200px; +} + +/* Overriding the track layout for playlists to include the album column */ +.track { + grid-template-columns: 40px 1fr 1fr auto auto; +} + +/* Style for the download albums button */ +#downloadAlbumsBtn { + background-color: rgba(255, 255, 255, 0.1); +} + +#downloadAlbumsBtn:hover { + background-color: rgba(255, 255, 255, 0.2); +} + +/* Mobile responsiveness adjustments */ +@media (max-width: 1024px) { + .track { + grid-template-columns: 40px 1fr auto auto; + } + + .track-album { + display: none; + } +} + +@media (max-width: 768px) { + .playlist-description { + max-width: 100%; + } + + #downloadAlbumsBtn { + margin-top: 0.5rem; + } +} + +@media (max-width: 480px) { + .track { + grid-template-columns: 30px 1fr auto; + } + + .playlist-description { + margin-bottom: 1rem; + } +} diff --git a/static/css/queue/queue.css b/static/css/queue/queue.css index 66ef27f..14ba98d 100644 --- a/static/css/queue/queue.css +++ b/static/css/queue/queue.css @@ -51,18 +51,29 @@ /* Cancel all button styling */ #cancelAllBtn { - background: #ff5555; + background: #8b0000; /* Dark blood red */ border: none; color: #fff; padding: 8px 12px; border-radius: 4px; cursor: pointer; - transition: background 0.3s ease; + transition: background 0.3s ease, transform 0.2s ease; font-size: 14px; + font-weight: 600; + display: flex; + align-items: center; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5); } #cancelAllBtn:hover { - background: #ff7777; + background: #a30000; /* Slightly lighter red on hover */ + transform: translateY(-1px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4); +} + +#cancelAllBtn:active { + transform: scale(0.98); } /* Close button for the queue sidebar */ @@ -78,11 +89,16 @@ color: #ffffff; font-size: 20px; cursor: pointer; - transition: background-color 0.3s ease; + transition: background-color 0.3s ease, transform 0.2s ease; } .close-btn:hover { background-color: #333; + transform: scale(1.05); +} + +.close-btn:active { + transform: scale(0.95); } /* Container for all queue items */ @@ -90,6 +106,24 @@ /* Allow the container to fill all available space in the sidebar */ flex: 1; overflow-y: auto; + padding-right: 5px; /* Add slight padding for scrollbar */ + scrollbar-width: thin; + scrollbar-color: #1DB954 rgba(255, 255, 255, 0.1); +} + +/* Custom scrollbar styles */ +#queueItems::-webkit-scrollbar { + width: 6px; +} + +#queueItems::-webkit-scrollbar-track { + background: rgba(255, 255, 255, 0.1); + border-radius: 10px; +} + +#queueItems::-webkit-scrollbar-thumb { + background-color: #1DB954; + border-radius: 10px; } /* Each download queue item */ @@ -98,33 +132,72 @@ padding: 15px; border-radius: 8px; margin-bottom: 15px; - transition: background-color 0.3s ease, transform 0.2s ease; + transition: all 0.3s ease; display: flex; flex-direction: column; gap: 6px; + position: relative; + border-left: 4px solid transparent; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); +} + +/* Animation only for newly added items */ +.queue-item-new { + animation: fadeIn 0.3s ease; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(5px); } + to { opacity: 1; transform: translateY(0); } } .queue-item:hover { background-color: #333; transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); } /* Title text in a queue item */ .queue-item .title { - font-weight: 500; + font-weight: 600; margin-bottom: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: #fff; + font-size: 14px; } /* Type indicator (e.g. track, album) */ .queue-item .type { - font-size: 12px; + font-size: 11px; color: #1DB954; text-transform: uppercase; - letter-spacing: 0.5px; + letter-spacing: 0.7px; + font-weight: 600; + background-color: rgba(29, 185, 84, 0.1); + padding: 3px 6px; + border-radius: 4px; + display: inline-block; + width: fit-content; +} + +/* Album type - for better visual distinction */ +.queue-item .type.album { + color: #4a90e2; + background-color: rgba(74, 144, 226, 0.1); +} + +/* Track type */ +.queue-item .type.track { + color: #1DB954; + background-color: rgba(29, 185, 84, 0.1); +} + +/* Playlist type */ +.queue-item .type.playlist { + color: #e67e22; + background-color: rgba(230, 126, 34, 0.1); } /* Log text for status messages */ @@ -133,19 +206,22 @@ color: #b3b3b3; line-height: 1.4; font-family: 'SF Mono', Menlo, monospace; + padding: 8px 0; + word-break: break-word; } /* Optional state indicators for each queue item */ -.queue-item--complete { - border-left: 4px solid #1DB954; +.queue-item--complete, +.queue-item.download-success { + border-left-color: #1DB954; } .queue-item--error { - border-left: 4px solid #ff5555; + border-left-color: #ff5555; } .queue-item--processing { - border-left: 4px solid #4a90e2; + border-left-color: #4a90e2; } /* Progress bar for downloads */ @@ -155,13 +231,16 @@ width: 0; transition: width 0.3s ease; margin-top: 8px; + border-radius: 2px; } /* Progress percentage text */ .progress-percent { text-align: right; font-weight: bold; + font-size: 12px; color: #1DB954; + margin-top: 4px; } /* Optional status message colors (if using state classes) */ @@ -197,12 +276,14 @@ /* Loading spinner style */ .loading-spinner { display: inline-block; - width: 16px; - height: 16px; - border: 3px solid rgba(255, 255, 255, 0.3); + width: 14px; + height: 14px; + border: 2px solid rgba(255, 255, 255, 0.3); border-radius: 50%; border-top-color: #1DB954; animation: spin 1s ease-in-out infinite; + margin-right: 6px; + vertical-align: middle; } @keyframes spin { @@ -220,6 +301,15 @@ /* Optionally constrain the overall size */ max-width: 24px; max-height: 24px; + position: absolute; + top: 10px; + right: 10px; + opacity: 0.7; + transition: opacity 0.2s ease, transform 0.2s ease; +} + +.cancel-btn:hover { + opacity: 1; } .cancel-btn img { @@ -237,28 +327,65 @@ transform: scale(0.9); } +/* Group header for multiple albums from same artist */ +.queue-group-header { + font-size: 14px; + color: #b3b3b3; + margin: 15px 0 10px; + padding-bottom: 8px; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + display: flex; + align-items: center; + justify-content: space-between; +} + +.queue-group-header span { + display: flex; + align-items: center; +} + +.queue-group-header span::before { + content: ''; + display: inline-block; + width: 10px; + height: 10px; + border-radius: 50%; + background-color: #1DB954; + margin-right: 8px; +} + /* ------------------------------- */ /* FOOTER & "SHOW MORE" BUTTON */ /* ------------------------------- */ #queueFooter { text-align: center; - padding-top: 10px; + padding-top: 15px; + border-top: 1px solid rgba(255, 255, 255, 0.1); + margin-top: 10px; } #queueFooter button { background: #1DB954; border: none; - padding: 8px 16px; - border-radius: 4px; + padding: 10px 18px; + border-radius: 20px; color: #fff; cursor: pointer; - transition: background 0.3s ease; + transition: all 0.3s ease; font-size: 14px; + font-weight: 500; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); } #queueFooter button:hover { background: #17a448; + transform: translateY(-1px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); +} + +#queueFooter button:active { + transform: scale(0.98); } /* -------------------------- */ @@ -287,35 +414,77 @@ /* Hover state for all error buttons */ .error-buttons button:hover { background: #333; + transform: translateY(-1px); +} + +.error-buttons button:active { + transform: scale(0.98); } /* Specific styles for the Close (X) error button */ .close-error-btn { - background: #ff5555; + background: #ff5555 !important; width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; - border-radius: 50%; - font-size: 20px; - padding: 0; + border-radius: 50% !important; + font-size: 18px !important; + padding: 0 !important; } .close-error-btn:hover { - background: #ff7777; + background: #ff7777 !important; } /* Specific styles for the Retry button */ .retry-btn { - background: #1DB954; - padding: 8px 16px; - border-radius: 4px; - font-weight: bold; + background: #1DB954 !important; + padding: 8px 16px !important; + border-radius: 20px !important; + font-weight: 500 !important; + flex: 1; } .retry-btn:hover { - background: #17a448; + background: #17a448 !important; +} + +/* Empty queue state */ +.queue-empty { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 200px; + color: #b3b3b3; + text-align: center; + padding: 20px; +} + +.queue-empty img { + width: 60px; + height: 60px; + margin-bottom: 15px; + opacity: 0.6; +} + +.queue-empty p { + font-size: 14px; + line-height: 1.5; +} + +/* Error notification in queue */ +.queue-error { + background-color: rgba(192, 57, 43, 0.1); + color: #ff5555; + padding: 10px 15px; + border-radius: 8px; + margin-bottom: 15px; + font-size: 14px; + border-left: 3px solid #ff5555; + animation: fadeIn 0.3s ease; } /* ------------------------------- */ @@ -336,8 +505,10 @@ /* Adjust header and title for smaller screens */ .sidebar-header { - flex-direction: column; - align-items: flex-start; + flex-direction: row; + align-items: center; + padding-bottom: 12px; + margin-bottom: 15px; } .sidebar-header h2 { @@ -362,4 +533,22 @@ .queue-item .type { font-size: 12px; } + + #cancelAllBtn { + padding: 6px 10px; + font-size: 12px; + } + + .error-buttons { + flex-direction: row; + } + + .close-error-btn { + width: 28px; + height: 28px; + } + + .retry-btn { + padding: 6px 12px !important; + } } diff --git a/static/css/track/track.css b/static/css/track/track.css index 35f4161..9ee57bb 100644 --- a/static/css/track/track.css +++ b/static/css/track/track.css @@ -235,7 +235,7 @@ a:focus { justify-content: center; } -/* Style the download button’s icon */ +/* Style the download button's icon */ .download-btn img { width: 20px; height: 20px; @@ -260,3 +260,101 @@ a:focus { text-align: center; } } + +/* Track page specific styles */ + +/* Track details formatting */ +.track-details { + display: flex; + gap: 1rem; + margin-top: 0.5rem; + color: var(--color-text-secondary); + font-size: 0.9rem; +} + +.track-detail-item { + display: flex; + align-items: center; +} + +/* Make explicit tag stand out if needed */ +#track-explicit:not(:empty) { + background-color: rgba(255, 255, 255, 0.1); + padding: 0.2rem 0.5rem; + border-radius: var(--radius-sm); + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +/* Loading indicator animation */ +.loading-indicator { + display: inline-block; + position: relative; + width: 80px; + height: 80px; +} + +.loading-indicator:after { + content: " "; + display: block; + width: 40px; + height: 40px; + margin: 8px; + border-radius: 50%; + border: 4px solid var(--color-primary); + border-color: var(--color-primary) transparent var(--color-primary) transparent; + animation: loading-rotation 1.2s linear infinite; +} + +@keyframes loading-rotation { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +/* Modern gradient for the track name */ +#track-name a { + background: linear-gradient(90deg, var(--color-primary), #2ecc71); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + display: inline-block; +} + +/* Ensure proper spacing for album and artist links */ +#track-artist a, +#track-album a { + transition: color 0.2s ease, text-decoration 0.2s ease; +} + +#track-artist a:hover, +#track-album a:hover { + color: var(--color-primary); +} + +/* Mobile-specific adjustments */ +@media (max-width: 768px) { + .track-details { + flex-direction: column; + gap: 0.25rem; + margin-bottom: 1rem; + } + + #track-name a { + font-size: 1.75rem; + } +} + +@media (max-width: 480px) { + #track-name a { + font-size: 1.5rem; + } + + .track-details { + margin-bottom: 1.5rem; + } +} diff --git a/static/images/album.svg b/static/images/album.svg new file mode 100644 index 0000000..c0c1074 --- /dev/null +++ b/static/images/album.svg @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/static/images/arrow-left.svg b/static/images/arrow-left.svg new file mode 100644 index 0000000..febbadc --- /dev/null +++ b/static/images/arrow-left.svg @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/static/images/music.svg b/static/images/music.svg new file mode 100644 index 0000000..054d6da --- /dev/null +++ b/static/images/music.svg @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/static/images/queue-empty.svg b/static/images/queue-empty.svg new file mode 100644 index 0000000..5695b55 --- /dev/null +++ b/static/images/queue-empty.svg @@ -0,0 +1,20 @@ + + \ No newline at end of file diff --git a/static/images/search.svg b/static/images/search.svg new file mode 100644 index 0000000..695916e --- /dev/null +++ b/static/images/search.svg @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/static/js/main.js b/static/js/main.js index d83cdec..4493080 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -1,428 +1,401 @@ // main.js import { downloadQueue } from './queue.js'; -document.addEventListener('DOMContentLoaded', () => { - const searchButton = document.getElementById('searchButton'); +document.addEventListener('DOMContentLoaded', function() { + // DOM elements const searchInput = document.getElementById('searchInput'); + const searchButton = document.getElementById('searchButton'); + const searchType = document.getElementById('searchType'); + const resultsContainer = document.getElementById('resultsContainer'); const queueIcon = document.getElementById('queueIcon'); - const searchType = document.getElementById('searchType'); // Ensure this element exists in your HTML + const emptyState = document.getElementById('emptyState'); + const loadingResults = document.getElementById('loadingResults'); - // Preselect the saved search type if available - const storedSearchType = localStorage.getItem('searchType'); - if (storedSearchType && searchType) { - searchType.value = storedSearchType; - } - - // Save the search type to local storage whenever it changes - if (searchType) { - searchType.addEventListener('change', () => { - localStorage.setItem('searchType', searchType.value); + // Initialize the queue + if (queueIcon) { + queueIcon.addEventListener('click', () => { + downloadQueue.toggleVisibility(); }); } - // Initialize queue icon - if (queueIcon) { - queueIcon.addEventListener('click', () => downloadQueue.toggleVisibility()); - } - - // Search functionality + // Add event listeners if (searchButton) { searchButton.addEventListener('click', performSearch); } - + if (searchInput) { - searchInput.addEventListener('keypress', (e) => { - if (e.key === 'Enter') performSearch(); + searchInput.addEventListener('keypress', function(e) { + if (e.key === 'Enter') { + performSearch(); + } }); + + // Auto-detect and handle pasted Spotify URLs + searchInput.addEventListener('input', function(e) { + const inputVal = e.target.value.trim(); + if (isSpotifyUrl(inputVal)) { + const details = getSpotifyResourceDetails(inputVal); + if (details) { + searchType.value = details.type; + } + } + }); + } + + // Check for URL parameters + const urlParams = new URLSearchParams(window.location.search); + const query = urlParams.get('q'); + const type = urlParams.get('type'); + + if (query) { + searchInput.value = query; + if (type && ['track', 'album', 'playlist', 'artist'].includes(type)) { + searchType.value = type; + } + performSearch(); + } else { + // Show empty state if no query + showEmptyState(true); + } + + /** + * Performs the search based on input values + */ + async function performSearch() { + const query = searchInput.value.trim(); + if (!query) return; + + // Handle direct Spotify URLs + if (isSpotifyUrl(query)) { + const details = getSpotifyResourceDetails(query); + if (details && details.id) { + // Redirect to the appropriate page + window.location.href = `/${details.type}/${details.id}`; + return; + } + } + + // Update URL without reloading page + const newUrl = `${window.location.pathname}?q=${encodeURIComponent(query)}&type=${searchType.value}`; + window.history.pushState({ path: newUrl }, '', newUrl); + + // Show loading state + showEmptyState(false); + showLoading(true); + resultsContainer.innerHTML = ''; + + try { + const url = `/api/search?q=${encodeURIComponent(query)}&search_type=${searchType.value}&limit=40`; + const response = await fetch(url); + + if (!response.ok) { + throw new Error('Network response was not ok'); + } + + const data = await response.json(); + + // Hide loading indicator + showLoading(false); + + // Render results + if (data && data.items && data.items.length > 0) { + resultsContainer.innerHTML = ''; + + data.items.forEach((item, index) => { + if (!item) return; // Skip null/undefined items + + const cardElement = createResultCard(item, searchType.value, index); + resultsContainer.appendChild(cardElement); + }); + + // Attach download handlers to the newly created cards + attachDownloadListeners(data.items); + } else { + // No results found + resultsContainer.innerHTML = ` +
No results found for "${query}"
+Error searching: ${error.message}
+
- Your download queue is empty
+