commit d0652e8c630134f16c298249fae1f343f383b4c7
parent c88e84f47a6760976db32400344a64d7de2fd07f
Author: Hunter
Date:   Tue, 28 Oct 2025 09:59:06 -0400

split out html, css, and js

Diffstat:
Mindex.html | 597+------------------------------------------------------------------------------
Ascript.js | 365+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Astyles.css | 224+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 591 insertions(+), 595 deletions(-)

diff --git a/index.html b/index.html @@ -5,233 +5,7 @@ <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>vibe capsule</title> - <style> - :root { - --icee: #dddddd; - --blueberry: #4c7ae6; - --bubblegum: #f421ff; - --asphalt: #080a0c; - --fog: #ddddddcc; - } - - * { - scrollbar-color: var(--fog) var(--asphalt); - } - - body { - margin: 0; - padding: 0; - font-family: "MS Sans Serif", Arial, sans-serif; - height: 100vh; - display: flex; - flex-direction: column; - font-size: 24px; - background-color: var(--asphalt); - } - - .window-container { - background: transparent; - height: 100%; - display: flex; - flex-direction: column; - } - - .player-container { - flex-grow: 1; - display: flex; - flex-direction: column; - overflow: hidden; - position: relative; - } - - .controls { - display: flex; - justify-content: center; - padding-bottom: 10px; - height: 52px; - background: transparent; - position: absolute; - bottom: 0; - left: 0; - right: 0; - box-sizing: border-box; - z-index: 3; - } - - .playlist { - flex-grow: 1; - overflow-y: auto; - padding-bottom: 62px; - cursor: initial; - } - - .playlist-item { - padding: 5px 10px; - display: flex; - justify-content: space-between; - align-items: center; - color: var(--icee); - min-height: 45px; - } - - .playlist-item-content { - flex-grow: 1; - display: flex; - flex-direction: column; - gap: 2px; - } - - .playlist-item-title { - font-size: 20px; - line-height: 1.2; - } - - .playlist-item-title.current { - font-weight: bold; - color: var(--blueberry); - } - - .playlist-item-artist { - font-size: 16px; - color: var(--fog); - line-height: 1.2; - } - - .playlist-item-artist.current { - font-weight: bold; - color: var(--blueberry); - opacity: 1; - } - - .playlist-item:hover { - background-color: var(--bubblegum); - cursor: pointer; - color: var(--asphalt); - } - - .playlist-item:hover .playlist-item-artist { - color: var(--asphalt); - } - - .playlist-item:hover .playlist-item-title.current, - .playlist-item:hover .playlist-item-artist.current { - color: var(--asphalt); - } - - .current-song { - text-align: left; - padding-left: 10px; - font-weight: bold; - color: var(--blueberry); - min-height: 35px; - display: flex; - align-items: center; - } - - .current-song span { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - #audioPlayer { - display: none; - } - - .progress-container { - width: 100%; - font-size: 24px; - cursor: grab; - margin-top: 3px; - height: 35px; - display: flex; - align-items: center; - padding: 0 12px; - box-sizing: border-box; - position: relative; - } - - .progress-container:hover { - cursor: grab; - } - - .progress-bar { - width: 100%; - height: 4px; - background-color: var(--icee); - position: relative; - border-radius: 1px; - --progress: 0%; - --circle-size: 16px; - } - - .progress-bar::before { - content: ''; - position: absolute; - left: 0; - top: 0; - height: 100%; - width: var(--progress); - background-color: var(--bubblegum); - border-radius: 1px; - transition: none; - } - - .progress-bar::after { - content: ''; - position: absolute; - left: calc(var(--progress) - var(--circle-size) / 2); - top: 50%; - transform: translateY(-50%); - width: var(--circle-size); - height: var(--circle-size); - background-color: var(--bubblegum); - border-radius: 50%; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); - transition: none; - } - - .progress-text { - display: none; - } - - .progress-text-left { - display: none; - } - - .progress-text-center { - display: none; - } - - .control-button { - width: 120px; - height: 42px; - background-size: contain; - background-repeat: no-repeat; - background-position: center; - border: none; - cursor: pointer; - margin: 0 5px; - border-radius: 4px; - } - - #playPause { - background-image: url('resources/play.png'); - } - - #playPause.pause { - background-image: url('resources/pause.png'); - } - - #prev { - background-image: url('resources/prev.png'); - } - - #next { - background-image: url('resources/next.png'); - } - </style> - + <link rel="stylesheet" href="styles.css"> </head> <body> <div class="window-container"> @@ -254,373 +28,6 @@ <audio id="audioPlayer"></audio> - <script> - const playPauseBtn = document.getElementById('playPause'); - const prevBtn = document.getElementById('prev'); - const nextBtn = document.getElementById('next'); - const playlist = document.getElementById('playlist'); - const currentSongDisplay = document.getElementById('currentSong'); - const progressBar = document.getElementById('progressBar'); - const progressContainer = document.getElementById('progressContainer'); - const audio = document.getElementById('audioPlayer'); - - let currentSongIndex = 0; - let isPlaying = false; - let progressInterval; - let playerReady = false; - let songs = []; - let animationFrameId = null; - let prePlaySeekTime = 0; - - // Load tracks from tracks.json - fetch('tracks.json') - .then(response => { - if (!response.ok) { - throw new Error('tracks.json not found'); - } - return response.json(); - }) - .then(data => { - songs = data; - if (songs.length > 0) { - playerReady = true; - updateCurrentSongDisplay(`Ready to play: ${songs[0].artist} - ${songs[0].title}`); - renderPlaylist(); - } else { - updateCurrentSongDisplay('No tracks found'); - } - }) - .catch(error => { - console.error('Error loading tracks:', error); - updateCurrentSongDisplay('Error: tracks.json not found. Run scan.py first.'); - }); - - // Audio event listeners - audio.addEventListener('play', () => { - startProgressBar(); - const song = songs[currentSongIndex]; - updateCurrentSongDisplay(song.looping ? - `Looping: ${song.artist} - ${song.title}` : - `Now playing: ${song.artist} - ${song.title}`); - - // Update media session metadata - if ('mediaSession' in navigator) { - navigator.mediaSession.metadata = new MediaMetadata({ - title: song.title, - artist: song.artist - }); - } - }); - - audio.addEventListener('pause', () => { - stopProgressBar(); - const song = songs[currentSongIndex]; - updateCurrentSongDisplay(`Paused: ${song.artist} - ${song.title}`); - }); - - audio.addEventListener('ended', () => { - if (songs[currentSongIndex].looping) { - audio.currentTime = 0; - audio.play(); - } else { - nextSong(); - } - }); - - audio.addEventListener('error', (e) => { - console.error('Audio error:', e); - updateCurrentSongDisplay(`Error loading: ${songs[currentSongIndex].filename}`); - // Try next song after a brief delay - setTimeout(() => nextSong(), 1000); - }); - - audio.addEventListener('loadedmetadata', () => { - resetProgressBar(); - }); - - function renderPlaylist() { - playlist.innerHTML = ''; - const currentDisplayText = currentSongDisplay.textContent; - const isInitialized = currentDisplayText !== 'No song playing'; - - songs.forEach((song, index) => { - const item = document.createElement('div'); - item.classList.add('playlist-item'); - - const contentDiv = document.createElement('div'); - contentDiv.classList.add('playlist-item-content'); - - const titleDiv = document.createElement('div'); - titleDiv.classList.add('playlist-item-title'); - if (isInitialized && index === currentSongIndex) { - titleDiv.classList.add('current'); - } - titleDiv.textContent = song.title; - - const artistDiv = document.createElement('div'); - artistDiv.classList.add('playlist-item-artist'); - if (isInitialized && index === currentSongIndex) { - artistDiv.classList.add('current'); - } - artistDiv.textContent = song.artist; - - contentDiv.appendChild(titleDiv); - contentDiv.appendChild(artistDiv); - - const loopIcon = document.createElement('span'); - loopIcon.textContent = '🔁'; - loopIcon.style.display = (song.looping || false) ? 'inline' : 'none'; - - item.appendChild(contentDiv); - item.appendChild(loopIcon); - item.addEventListener('click', () => toggleLooping(index)); - playlist.appendChild(item); - }); - } - - function toggleLooping(index) { - if (!playerReady) return; - if (index === currentSongIndex) { - // If it's ready to play but not playing yet, just play it - if (!isPlaying && currentSongDisplay.textContent.includes('Ready to play')) { - audio.play(); - isPlaying = true; - updatePlayPauseButton(); - return; - } - // Toggle looping - songs[index].looping = !(songs[index].looping || false); - renderPlaylist(); - - const song = songs[index]; - // Update the display text based on current state - if (isPlaying) { - updateCurrentSongDisplay(song.looping ? - `Looping: ${song.artist} - ${song.title}` : - `Now playing: ${song.artist} - ${song.title}`); - } else { - updateCurrentSongDisplay(`Paused: ${song.artist} - ${song.title}`); - } - } else { - playSong(index); - } - } - - function playSong(index) { - if (!playerReady) return; - // Clear looping from all songs except the new one if it was already looping - const wasLooping = songs[index].looping || false; - songs.forEach(song => song.looping = false); - if (wasLooping) { - songs[index].looping = true; - } - - currentSongIndex = index; - const song = songs[currentSongIndex]; - audio.src = `tracks/${song.filename}`; - audio.play(); - isPlaying = true; - updatePlayPauseButton(); - renderPlaylist(); - } - - function updateCurrentSongDisplay(text) { - currentSongDisplay.innerHTML = `<span>${text}</span>`; - } - - function togglePlayPause() { - if (!playerReady) return; - if (isPlaying) { - audio.pause(); - isPlaying = false; - } else { - // If no song is loaded, load the first one - if (!audio.src || audio.src === '') { - playSong(currentSongIndex); - } else { - audio.play(); - isPlaying = true; - } - } - updatePlayPauseButton(); - } - - function updatePlayPauseButton() { - playPauseBtn.classList.toggle('pause', isPlaying); - } - - function nextSong() { - if (!playerReady) return; - currentSongIndex = (currentSongIndex + 1) % songs.length; - playSong(currentSongIndex); - } - - function prevSong() { - if (!playerReady) return; - if (audio.currentTime <= 3) { - currentSongIndex = (currentSongIndex - 1 + songs.length) % songs.length; - playSong(currentSongIndex); - } else { - audio.currentTime = 0; - } - } - - function startProgressBar() { - stopProgressBar(); - - function animate() { - updateProgressBar(); - animationFrameId = requestAnimationFrame(animate); - } - - animate(); - } - - function stopProgressBar() { - if (animationFrameId !== null) { - cancelAnimationFrame(animationFrameId); - animationFrameId = null; - } - } - - function resetProgressBar() { - progressBar.style.setProperty('--progress', '0%'); - } - - function updateProgressBar() { - if (audio.duration && !isDragging) { - const currentTime = audio.currentTime; - const duration = audio.duration; - const progressPercentage = (currentTime / duration) * 100; - const displayPercentage = isNaN(progressPercentage) ? 0 : progressPercentage; - - progressBar.style.setProperty('--progress', `${displayPercentage}%`); - } - } - - let isDragging = false; - let wasPlayingBeforeDrag = false; - let pendingSeekPercentage = null; - - function updateVisualProgress(event) { - if (!playerReady) return; - - const rect = progressBar.getBoundingClientRect(); - const clickPosition = event.clientX - rect.left; - const clickPercentage = Math.max(0, Math.min(1, clickPosition / rect.width)); - - progressBar.style.setProperty('--progress', `${clickPercentage * 100}%`); - return clickPercentage; - } - - function applySeek(clickPercentage) { - if (!playerReady) return; - - // If audio hasn't been loaded yet, load it but don't play - if (!audio.src || audio.src === '') { - const song = songs[currentSongIndex]; - audio.src = `tracks/${song.filename}`; - - // Wait for metadata to be loaded before seeking - audio.addEventListener('loadedmetadata', function setInitialTime() { - const duration = audio.duration; - const seekTime = duration * clickPercentage; - audio.currentTime = seekTime; - prePlaySeekTime = seekTime; - audio.removeEventListener('loadedmetadata', setInitialTime); - }, { once: true }); - } else if (audio.duration) { - const duration = audio.duration; - const seekTime = duration * clickPercentage; - audio.currentTime = seekTime; - prePlaySeekTime = seekTime; - } - } - - function onProgressMouseDown(event) { - if (!playerReady) return; - isDragging = true; - wasPlayingBeforeDrag = isPlaying; - progressContainer.style.cursor = 'grabbing'; - document.body.style.cursor = 'grabbing'; - pendingSeekPercentage = updateVisualProgress(event); - event.preventDefault(); - } - - function onProgressMouseMove(event) { - if (isDragging) { - pendingSeekPercentage = updateVisualProgress(event); - } - } - - function onProgressMouseUp(event) { - if (isDragging) { - isDragging = false; - progressContainer.style.cursor = ''; - document.body.style.cursor = ''; - - // Apply the seek now that drag is complete - if (pendingSeekPercentage !== null) { - applySeek(pendingSeekPercentage); - pendingSeekPercentage = null; - } - - // If it was "Ready to play" (not playing before), start playing now - if (!wasPlayingBeforeDrag && !isPlaying && audio.src) { - audio.play(); - isPlaying = true; - updatePlayPauseButton(); - } - } - } - - playPauseBtn.addEventListener('click', togglePlayPause); - nextBtn.addEventListener('click', nextSong); - prevBtn.addEventListener('click', prevSong); - progressContainer.addEventListener('mousedown', onProgressMouseDown); - document.addEventListener('mousemove', onProgressMouseMove); - document.addEventListener('mouseup', onProgressMouseUp); - - // Keyboard controls - document.addEventListener('keydown', function(event) { - if (!playerReady) return; - - // Spacebar: play/pause - if (event.code === 'Space') { - event.preventDefault(); - togglePlayPause(); - } - }); - - // Media key controls - navigator.mediaSession.metadata = new MediaMetadata({ - title: 'vibe capsule', - artist: 'MP3 Player' - }); - - navigator.mediaSession.setActionHandler('play', () => { - if (playerReady && !isPlaying) { - togglePlayPause(); - } - }); - - navigator.mediaSession.setActionHandler('pause', () => { - if (playerReady && isPlaying) { - togglePlayPause(); - } - }); - - navigator.mediaSession.setActionHandler('previoustrack', () => { - if (playerReady) { - prevSong(); - } - }); - - navigator.mediaSession.setActionHandler('nexttrack', () => { - if (playerReady) { - nextSong(); - } - }); - </script> + <script src="script.js"></script> </body> </html> diff --git a/script.js b/script.js @@ -0,0 +1,365 @@ +const playPauseBtn = document.getElementById('playPause'); +const prevBtn = document.getElementById('prev'); +const nextBtn = document.getElementById('next'); +const playlist = document.getElementById('playlist'); +const currentSongDisplay = document.getElementById('currentSong'); +const progressBar = document.getElementById('progressBar'); +const progressContainer = document.getElementById('progressContainer'); +const audio = document.getElementById('audioPlayer'); + +let currentSongIndex = 0; +let isPlaying = false; +let progressInterval; +let playerReady = false; +let songs = []; +let animationFrameId = null; +let prePlaySeekTime = 0; + +// Load tracks from tracks.json +fetch('tracks.json') + .then(response => { + if (!response.ok) { + throw new Error('tracks.json not found'); + } + return response.json(); + }) + .then(data => { + songs = data; + if (songs.length > 0) { + playerReady = true; + updateCurrentSongDisplay(`Ready to play: ${songs[0].artist} - ${songs[0].title}`); + renderPlaylist(); + } else { + updateCurrentSongDisplay('No tracks found'); + } + }) + .catch(error => { + console.error('Error loading tracks:', error); + updateCurrentSongDisplay('Error: tracks.json not found. Run scan.py first.'); + }); + +// Audio event listeners +audio.addEventListener('play', () => { + startProgressBar(); + const song = songs[currentSongIndex]; + updateCurrentSongDisplay(song.looping ? + `Looping: ${song.artist} - ${song.title}` : + `Now playing: ${song.artist} - ${song.title}`); + + // Update media session metadata + if ('mediaSession' in navigator) { + navigator.mediaSession.metadata = new MediaMetadata({ + title: song.title, + artist: song.artist + }); + } +}); + +audio.addEventListener('pause', () => { + stopProgressBar(); + const song = songs[currentSongIndex]; + updateCurrentSongDisplay(`Paused: ${song.artist} - ${song.title}`); +}); + +audio.addEventListener('ended', () => { + if (songs[currentSongIndex].looping) { + audio.currentTime = 0; + audio.play(); + } else { + nextSong(); + } +}); + +audio.addEventListener('error', (e) => { + console.error('Audio error:', e); + updateCurrentSongDisplay(`Error loading: ${songs[currentSongIndex].filename}`); + // Try next song after a brief delay + setTimeout(() => nextSong(), 1000); +}); + +audio.addEventListener('loadedmetadata', () => { + resetProgressBar(); +}); + +function renderPlaylist() { + playlist.innerHTML = ''; + const currentDisplayText = currentSongDisplay.textContent; + const isInitialized = currentDisplayText !== 'No song playing'; + + songs.forEach((song, index) => { + const item = document.createElement('div'); + item.classList.add('playlist-item'); + + const contentDiv = document.createElement('div'); + contentDiv.classList.add('playlist-item-content'); + + const titleDiv = document.createElement('div'); + titleDiv.classList.add('playlist-item-title'); + if (isInitialized && index === currentSongIndex) { + titleDiv.classList.add('current'); + } + titleDiv.textContent = song.title; + + const artistDiv = document.createElement('div'); + artistDiv.classList.add('playlist-item-artist'); + if (isInitialized && index === currentSongIndex) { + artistDiv.classList.add('current'); + } + artistDiv.textContent = song.artist; + + contentDiv.appendChild(titleDiv); + contentDiv.appendChild(artistDiv); + + const loopIcon = document.createElement('span'); + loopIcon.textContent = '🔁'; + loopIcon.style.display = (song.looping || false) ? 'inline' : 'none'; + + item.appendChild(contentDiv); + item.appendChild(loopIcon); + item.addEventListener('click', () => toggleLooping(index)); + playlist.appendChild(item); + }); +} + +function toggleLooping(index) { + if (!playerReady) return; + if (index === currentSongIndex) { + if (!isPlaying && currentSongDisplay.textContent.includes('Ready to play')) { + audio.play(); + isPlaying = true; + updatePlayPauseButton(); + return; + } + // Toggle looping + songs[index].looping = !(songs[index].looping || false); + renderPlaylist(); + + const song = songs[index]; + // Update the display text based on current state + if (isPlaying) { + updateCurrentSongDisplay(song.looping ? + `Looping: ${song.artist} - ${song.title}` : + `Now playing: ${song.artist} - ${song.title}`); + } else { + updateCurrentSongDisplay(`Paused: ${song.artist} - ${song.title}`); + } + } else { + playSong(index); + } +} + +function playSong(index) { + if (!playerReady) return; + // Clear looping from all songs except the new one if it was already looping + const wasLooping = songs[index].looping || false; + songs.forEach(song => song.looping = false); + if (wasLooping) { + songs[index].looping = true; + } + + currentSongIndex = index; + const song = songs[currentSongIndex]; + audio.src = `tracks/${song.filename}`; + audio.play(); + isPlaying = true; + updatePlayPauseButton(); + renderPlaylist(); +} + +function updateCurrentSongDisplay(text) { + currentSongDisplay.innerHTML = `<span>${text}</span>`; +} + +function togglePlayPause() { + if (!playerReady) return; + if (isPlaying) { + audio.pause(); + isPlaying = false; + } else { + // If no song is loaded, load the first one + if (!audio.src || audio.src === '') { + playSong(currentSongIndex); + } else { + audio.play(); + isPlaying = true; + } + } + updatePlayPauseButton(); +} + +function updatePlayPauseButton() { + playPauseBtn.classList.toggle('pause', isPlaying); +} + +function nextSong() { + if (!playerReady) return; + currentSongIndex = (currentSongIndex + 1) % songs.length; + playSong(currentSongIndex); +} + +function prevSong() { + if (!playerReady) return; + if (audio.currentTime <= 3) { + currentSongIndex = (currentSongIndex - 1 + songs.length) % songs.length; + playSong(currentSongIndex); + } else { + audio.currentTime = 0; + } +} + +function startProgressBar() { + stopProgressBar(); + + function animate() { + updateProgressBar(); + animationFrameId = requestAnimationFrame(animate); + } + + animate(); +} + +function stopProgressBar() { + if (animationFrameId !== null) { + cancelAnimationFrame(animationFrameId); + animationFrameId = null; + } +} + +function resetProgressBar() { + progressBar.style.setProperty('--progress', '0%'); +} + +function updateProgressBar() { + if (audio.duration && !isDragging) { + const currentTime = audio.currentTime; + const duration = audio.duration; + const progressPercentage = (currentTime / duration) * 100; + const displayPercentage = isNaN(progressPercentage) ? 0 : progressPercentage; + + progressBar.style.setProperty('--progress', `${displayPercentage}%`); + } +} + +let isDragging = false; +let wasPlayingBeforeDrag = false; +let pendingSeekPercentage = null; + +function updateVisualProgress(event) { + if (!playerReady) return; + + const rect = progressBar.getBoundingClientRect(); + const clickPosition = event.clientX - rect.left; + const clickPercentage = Math.max(0, Math.min(1, clickPosition / rect.width)); + + progressBar.style.setProperty('--progress', `${clickPercentage * 100}%`); + return clickPercentage; +} + +function applySeek(clickPercentage) { + if (!playerReady) return; + + // If audio hasn't been loaded yet, load it but don't play + if (!audio.src || audio.src === '') { + const song = songs[currentSongIndex]; + audio.src = `tracks/${song.filename}`; + + // Wait for metadata to be loaded before seeking + audio.addEventListener('loadedmetadata', function setInitialTime() { + const duration = audio.duration; + const seekTime = duration * clickPercentage; + audio.currentTime = seekTime; + prePlaySeekTime = seekTime; + audio.removeEventListener('loadedmetadata', setInitialTime); + }, { once: true }); + } else if (audio.duration) { + const duration = audio.duration; + const seekTime = duration * clickPercentage; + audio.currentTime = seekTime; + prePlaySeekTime = seekTime; + } +} + +function onProgressMouseDown(event) { + if (!playerReady) return; + isDragging = true; + wasPlayingBeforeDrag = isPlaying; + progressContainer.style.cursor = 'grabbing'; + document.body.style.cursor = 'grabbing'; + pendingSeekPercentage = updateVisualProgress(event); + event.preventDefault(); +} + +function onProgressMouseMove(event) { + if (isDragging) { + pendingSeekPercentage = updateVisualProgress(event); + } +} + +function onProgressMouseUp(event) { + if (isDragging) { + isDragging = false; + progressContainer.style.cursor = ''; + document.body.style.cursor = ''; + + // Apply the seek now that drag is complete + if (pendingSeekPercentage !== null) { + applySeek(pendingSeekPercentage); + pendingSeekPercentage = null; + } + + // If it was "Ready to play" (not playing before), start playing now + if (!wasPlayingBeforeDrag && !isPlaying && audio.src) { + audio.play(); + isPlaying = true; + updatePlayPauseButton(); + } + } +} + +playPauseBtn.addEventListener('click', togglePlayPause); +nextBtn.addEventListener('click', nextSong); +prevBtn.addEventListener('click', prevSong); +progressContainer.addEventListener('mousedown', onProgressMouseDown); +document.addEventListener('mousemove', onProgressMouseMove); +document.addEventListener('mouseup', onProgressMouseUp); + +// Keyboard controls +document.addEventListener('keydown', function(event) { + if (!playerReady) return; + + // Spacebar: play/pause + if (event.code === 'Space') { + event.preventDefault(); + togglePlayPause(); + } +}); + +// Media key controls +navigator.mediaSession.metadata = new MediaMetadata({ + title: 'vibe capsule', + artist: 'MP3 Player' +}); + +navigator.mediaSession.setActionHandler('play', () => { + if (playerReady && !isPlaying) { + togglePlayPause(); + } +}); + +navigator.mediaSession.setActionHandler('pause', () => { + if (playerReady && isPlaying) { + togglePlayPause(); + } +}); + +navigator.mediaSession.setActionHandler('previoustrack', () => { + if (playerReady) { + prevSong(); + } +}); + +navigator.mediaSession.setActionHandler('nexttrack', () => { + if (playerReady) { + nextSong(); + } +}); diff --git a/styles.css b/styles.css @@ -0,0 +1,224 @@ +:root { + --icee: #dddddd; + --blueberry: #4c7ae6; + --bubblegum: #f421ff; + --asphalt: #080a0c; + --fog: #ddddddcc; +} + +* { + scrollbar-color: var(--fog) var(--asphalt); +} + +body { + margin: 0; + padding: 0; + font-family: "MS Sans Serif", Arial, sans-serif; + height: 100vh; + display: flex; + flex-direction: column; + font-size: 24px; + background-color: var(--asphalt); +} + +.window-container { + background: transparent; + height: 100%; + display: flex; + flex-direction: column; +} + +.player-container { + flex-grow: 1; + display: flex; + flex-direction: column; + overflow: hidden; + position: relative; +} + +.controls { + display: flex; + justify-content: center; + padding-bottom: 10px; + height: 52px; + background: transparent; + position: absolute; + bottom: 0; + left: 0; + right: 0; + box-sizing: border-box; + z-index: 3; +} + +.playlist { + flex-grow: 1; + overflow-y: auto; + padding-bottom: 62px; + cursor: initial; +} + +.playlist-item { + padding: 5px 10px; + display: flex; + justify-content: space-between; + align-items: center; + color: var(--icee); + min-height: 45px; +} + +.playlist-item-content { + flex-grow: 1; + display: flex; + flex-direction: column; + gap: 2px; +} + +.playlist-item-title { + font-size: 20px; + line-height: 1.2; +} + +.playlist-item-title.current { + font-weight: bold; + color: var(--blueberry); +} + +.playlist-item-artist { + font-size: 16px; + color: var(--fog); + line-height: 1.2; +} + +.playlist-item-artist.current { + font-weight: bold; + color: var(--blueberry); + opacity: 1; +} + +.playlist-item:hover { + background-color: var(--bubblegum); + cursor: pointer; + color: var(--asphalt); +} + +.playlist-item:hover .playlist-item-artist { + color: var(--asphalt); +} + +.playlist-item:hover .playlist-item-title.current, +.playlist-item:hover .playlist-item-artist.current { + color: var(--asphalt); +} + +.current-song { + text-align: left; + padding-left: 10px; + font-weight: bold; + color: var(--blueberry); + min-height: 35px; + display: flex; + align-items: center; +} + +.current-song span { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +#audioPlayer { + display: none; +} + +.progress-container { + width: 100%; + font-size: 24px; + cursor: grab; + margin-top: 3px; + height: 35px; + display: flex; + align-items: center; + padding: 0 12px; + box-sizing: border-box; + position: relative; +} + +.progress-container:hover { + cursor: grab; +} + +.progress-bar { + width: 100%; + height: 4px; + background-color: var(--icee); + position: relative; + border-radius: 1px; + --progress: 0%; + --circle-size: 16px; +} + +.progress-bar::before { + content: ''; + position: absolute; + left: 0; + top: 0; + height: 100%; + width: var(--progress); + background-color: var(--bubblegum); + border-radius: 1px; + transition: none; +} + +.progress-bar::after { + content: ''; + position: absolute; + left: calc(var(--progress) - var(--circle-size) / 2); + top: 50%; + transform: translateY(-50%); + width: var(--circle-size); + height: var(--circle-size); + background-color: var(--bubblegum); + border-radius: 50%; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); + transition: none; +} + +.progress-text { + display: none; +} + +.progress-text-left { + display: none; +} + +.progress-text-center { + display: none; +} + +.control-button { + width: 120px; + height: 42px; + background-size: contain; + background-repeat: no-repeat; + background-position: center; + border: none; + cursor: pointer; + margin: 0 5px; + border-radius: 4px; +} + +#playPause { + background-image: url('resources/play.png'); +} + +#playPause.pause { + background-image: url('resources/pause.png'); +} + +#prev { + background-image: url('resources/prev.png'); +} + +#next { + background-image: url('resources/next.png'); +}