Merge pull request #45 from zzstoatzz/feat/player-improvements

feat: improve player UI cosmetics

authored by zzstoatzz.io and committed by GitHub f8eec832 29052593

Changed files
+60 -17
frontend
src
lib
components
+60 -17
frontend/src/lib/components/Player.svelte
··· 6 6 let formattedCurrentTime = $derived(formatTime(player.currentTime)); 7 7 let formattedDuration = $derived(formatTime(player.duration)); 8 8 9 + // compute progress percentage for seek bar styling 10 + let progressPercent = $derived.by(() => { 11 + if (!player.duration || player.duration === 0) return 0; 12 + return (player.currentTime / player.duration) * 100; 13 + }); 14 + 9 15 // volume state indicators 10 16 let volumeState = $derived.by(() => { 11 17 if (player.volume === 0) return 'muted'; ··· 93 99 <div class="player-title" class:scrolling={player.currentTrack.title.length > 30}> 94 100 <span>{player.currentTrack.title}</span> 95 101 </div> 96 - <a href="/u/{player.currentTrack.artist_handle}" class="player-artist-link"> 97 - {player.currentTrack.artist} 98 - </a> 102 + <div class="player-metadata"> 103 + <a href="/u/{player.currentTrack.artist_handle}" class="player-artist-link"> 104 + {player.currentTrack.artist} 105 + </a> 106 + {#if player.currentTrack.album} 107 + <span class="metadata-separator">•</span> 108 + <span class="player-album">{player.currentTrack.album}</span> 109 + {/if} 110 + </div> 99 111 </div> 100 112 101 113 <div class="player-controls"> ··· 124 136 min="0" 125 137 max={player.duration || 0} 126 138 bind:value={player.currentTime} 139 + style="--progress: {progressPercent}%" 127 140 /> 128 141 <span class="time">{formattedDuration}</span> 129 142 </div> ··· 218 231 } 219 232 } 220 233 221 - .player-artist-link { 234 + .player-metadata { 235 + display: flex; 236 + align-items: center; 237 + gap: 0.5rem; 222 238 color: #909090; 223 239 font-size: 0.9rem; 240 + white-space: nowrap; 241 + overflow: hidden; 242 + } 243 + 244 + .player-artist-link { 245 + color: #909090; 224 246 text-decoration: none; 225 - display: block; 226 247 transition: color 0.2s; 227 - white-space: nowrap; 228 - overflow: hidden; 229 - text-overflow: ellipsis; 248 + flex-shrink: 0; 230 249 } 231 250 232 251 .player-artist-link:hover { 233 252 color: var(--accent); 234 253 } 235 254 255 + .metadata-separator { 256 + color: #606060; 257 + flex-shrink: 0; 258 + } 259 + 260 + .player-album { 261 + color: #808080; 262 + overflow: hidden; 263 + text-overflow: ellipsis; 264 + white-space: nowrap; 265 + } 266 + 236 267 .player-controls { 237 268 flex: 1; 238 269 display: flex; ··· 324 355 cursor: pointer; 325 356 } 326 357 327 - input[type="range"]::-webkit-slider-track { 328 - background: #333; 358 + input[type="range"]::-webkit-slider-runnable-track { 359 + background: linear-gradient( 360 + to right, 361 + color-mix(in srgb, var(--accent) 60%, transparent) 0%, 362 + color-mix(in srgb, var(--accent) 60%, transparent) var(--progress, 0%), 363 + color-mix(in srgb, var(--accent) 20%, transparent) var(--progress, 0%), 364 + color-mix(in srgb, var(--accent) 20%, transparent) 100% 365 + ); 329 366 height: 4px; 330 367 border-radius: 2px; 331 368 } ··· 342 379 } 343 380 344 381 input[type="range"]::-webkit-slider-thumb:hover { 345 - background: #8ab3ff; 382 + background: var(--accent-hover); 346 383 transform: scale(1.2); 347 - box-shadow: 0 0 0 4px rgba(106, 159, 255, 0.2); 384 + box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 20%, transparent); 348 385 } 349 386 350 387 input[type="range"].muted::-webkit-slider-thumb { ··· 353 390 354 391 input[type="range"].max::-webkit-slider-thumb { 355 392 background: var(--accent); 356 - box-shadow: 0 0 0 4px rgba(106, 159, 255, 0.3); 393 + box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 30%, transparent); 357 394 } 358 395 359 396 input[type="range"]::-moz-range-track { 360 - background: #333; 397 + background: color-mix(in srgb, var(--accent) 20%, transparent); 398 + height: 4px; 399 + border-radius: 2px; 400 + } 401 + 402 + input[type="range"]::-moz-range-progress { 403 + background: color-mix(in srgb, var(--accent) 60%, transparent); 361 404 height: 4px; 362 405 border-radius: 2px; 363 406 } ··· 372 415 } 373 416 374 417 input[type="range"]::-moz-range-thumb:hover { 375 - background: #8ab3ff; 418 + background: var(--accent-hover); 376 419 transform: scale(1.2); 377 - box-shadow: 0 0 0 4px rgba(106, 159, 255, 0.2); 420 + box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 20%, transparent); 378 421 } 379 422 380 423 input[type="range"].muted::-moz-range-thumb { ··· 383 426 384 427 input[type="range"].max::-moz-range-thumb { 385 428 background: var(--accent); 386 - box-shadow: 0 0 0 4px rgba(106, 159, 255, 0.3); 429 + box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 30%, transparent); 387 430 } 388 431 389 432 @media (max-width: 768px) {