diff --git a/app/app.css b/app/app.css index 846ca44..59819e9 100644 --- a/app/app.css +++ b/app/app.css @@ -118,11 +118,13 @@ nav ul li { } nav ul li.active { border-top-color: var(--primary); + color: var(--primary); } #player { display: flex; flex-direction: row; align-items: center; + align-items: stretch; } #player:not([data-state=play]) .pause { display: none; @@ -147,11 +149,10 @@ nav ul li.active { } #player .info { flex-grow: 1; - align-self: stretch; overflow: hidden; display: flex; flex-direction: column; - justify-content: space-evenly; + justify-content: space-around; } #player .title, #player .subtitle { @@ -159,6 +160,9 @@ nav ul li.active { overflow: hidden; text-overflow: ellipsis; } +#player [type=range] { + margin: 0; +} #player .timeline { display: flex; flex-direction: row; @@ -167,18 +171,15 @@ nav ul li.active { #player .timeline .duration, #player .timeline .elapsed { flex-basis: 5ch; + text-align: center; } -#player .timeline .duration { - text-align: right; -} -#player .timeline progress { +#player .timeline [type=range] { flex-grow: 1; - height: 8px; } #player .controls { display: flex; flex-direction: column; - flex-basis: 160px; + justify-content: space-around; } #player .controls .playback { display: flex; @@ -198,13 +199,11 @@ nav ul li.active { flex-direction: row; align-items: center; } -#player .controls .volume .icon { +#player .controls .volume .mute { margin-right: 4px; } -#player .controls .volume meter { - flex-grow: 1; - vertical-align: top; - height: 8px; +#player .controls .volume [type=range] { + width: 132px; } #player .misc { display: flex; @@ -688,6 +687,10 @@ nav ul li.active { flex-direction: row; align-items: center; } +#settings label [type=radio], +#settings label [type=checkbox] { + margin: 0 4px 0 0; +} .search { display: flex; flex-direction: row; @@ -726,13 +729,13 @@ nav ul li.active { --icon-spacing: 4px; } :root[data-theme=light] { - --fg: #000; - --bg: #fff; - --bg-alt: #eee; + --fg: #333; + --bg: #f0f0f0; + --bg-alt: #e0e0e0; --shadow: none; } :root[data-theme=dark] { - --fg: #fff; + --fg: #f0f0f0; --bg: #333; --bg-alt: #555; --shadow: 0 1px 1px rgba(0, 0, 0, 0.8); diff --git a/app/css/nav.less b/app/css/nav.less index c4bde31..308f2eb 100644 --- a/app/css/nav.less +++ b/app/css/nav.less @@ -19,6 +19,7 @@ nav ul { &.active { border-top-color: var(--primary); + color: var(--primary); } } } diff --git a/app/css/player.less b/app/css/player.less index 535a000..79b64ba 100644 --- a/app/css/player.less +++ b/app/css/player.less @@ -1,5 +1,6 @@ #player { .flex-row; + align-items: stretch; &:not([data-state=play]) .pause { display: none; } &[data-state=play] .play { display: none; } @@ -20,33 +21,32 @@ .info { flex-grow: 1; - align-self: stretch; overflow: hidden; .flex-column; - justify-content: space-evenly; + justify-content: space-around; } .title, .subtitle { .long-line; } + [type=range] { margin: 0; } + .timeline { .flex-row; .duration, .elapsed { flex-basis: 5ch; + text-align: center; } - .duration { text-align: right; } - - progress { + [type=range] { flex-grow: 1; - height: 8px; } } .controls { .flex-column; - flex-basis: 64px + 2*48px; + justify-content: space-around; .playback { .flex-row; @@ -64,18 +64,14 @@ .volume { .flex-row; - .icon { + .mute { margin-right: 4px; } - meter { - flex-grow: 1; - vertical-align: top; - height: 8px; + [type=range] { + width: 132px; } } - - } .misc { diff --git a/app/css/settings.less b/app/css/settings.less index a1bb86c..1ae103d 100644 --- a/app/css/settings.less +++ b/app/css/settings.less @@ -21,5 +21,9 @@ label { .flex-row; + + [type=radio], [type=checkbox] { + margin: 0 4px 0 0; + } } } \ No newline at end of file diff --git a/app/css/variables.less b/app/css/variables.less index 4a89a02..36efd2a 100644 --- a/app/css/variables.less +++ b/app/css/variables.less @@ -4,14 +4,14 @@ } :root[data-theme=light] { - --fg: #000; - --bg: #fff; - --bg-alt: #eee; + --fg: #333; + --bg: #f0f0f0; + --bg-alt: #e0e0e0; --shadow: none; } :root[data-theme=dark] { - --fg: #fff; + --fg: #f0f0f0; --bg: #333; --bg-alt: #555; --shadow: 0 1px 1px rgba(0, 0, 0, 0.8); diff --git a/app/icons/volume-off.svg b/app/icons/volume-off.svg new file mode 100644 index 0000000..01e450f --- /dev/null +++ b/app/icons/volume-off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/index.html b/app/index.html index ed6a878..901f1ca 100644 --- a/app/index.html +++ b/app/index.html @@ -15,7 +15,7 @@
- +
@@ -27,7 +27,8 @@
- + +
diff --git a/app/js/app.js b/app/js/app.js index 5d3483d..57dd3e2 100644 --- a/app/js/app.js +++ b/app/js/app.js @@ -45,7 +45,11 @@ function onHashChange(e) { async function init() { initIcons(); - await mpd.init(); + try { + await mpd.init(); + } catch (e) { + console.error(e); + } nav.init(document.querySelector("nav")); for (let id in components) { diff --git a/app/js/lib/icons.js b/app/js/lib/icons.js index be2e4c5..a37532e 100644 --- a/app/js/lib/icons.js +++ b/app/js/lib/icons.js @@ -1,93 +1,96 @@ let ICONS={}; -ICONS["settings"] = ` - -`; -ICONS["library-music"] = ` - -`; -ICONS["pause"] = ` - -`; -ICONS["rewind"] = ` - -`; -ICONS["play"] = ` - +ICONS["playlist-music"] = ` + `; ICONS["play-circle-outline"] = ` `; -ICONS["album"] = ` - -`; -ICONS["plus"] = ` - -`; -ICONS["close-circle"] = ` - -`; -ICONS["minus"] = ` - -`; -ICONS["volume-high"] = ` - +ICONS["folder"] = ` + `; ICONS["shuffle"] = ` `; -ICONS["content-save"] = ` - -`; -ICONS["account-multiple"] = ` - -`; -ICONS["fast-forward"] = ` - -`; -ICONS["close"] = ` - -`; -ICONS["magnify"] = ` - -`; -ICONS["pause-circle"] = ` - -`; -ICONS["minus-circle"] = ` - -`; -ICONS["plus-circle"] = ` - -`; -ICONS["minus-circle-outline"] = ` - -`; -ICONS["playlist-music"] = ` - -`; -ICONS["music"] = ` - -`; -ICONS["repeat"] = ` - -`; -ICONS["folder"] = ` - -`; -ICONS["close-circle-outline"] = ` - -`; -ICONS["pause-circle-outline"] = ` - -`; -ICONS["plus-circle-outline"] = ` - +ICONS["artist"] = ` + `; ICONS["download"] = ` `; -ICONS["artist"] = ` - +ICONS["minus-circle"] = ` + +`; +ICONS["magnify"] = ` + +`; +ICONS["rewind"] = ` + +`; +ICONS["account-multiple"] = ` + +`; +ICONS["settings"] = ` + +`; +ICONS["pause"] = ` + +`; +ICONS["pause-circle"] = ` + +`; +ICONS["close-circle-outline"] = ` + +`; +ICONS["volume-off"] = ` + +`; +ICONS["close"] = ` + +`; +ICONS["music"] = ` + +`; +ICONS["minus"] = ` + +`; +ICONS["close-circle"] = ` + +`; +ICONS["repeat"] = ` + +`; +ICONS["play"] = ` + +`; +ICONS["pause-circle-outline"] = ` + +`; +ICONS["plus"] = ` + +`; +ICONS["content-save"] = ` + +`; +ICONS["library-music"] = ` + +`; +ICONS["fast-forward"] = ` + +`; +ICONS["minus-circle-outline"] = ` + +`; +ICONS["volume-high"] = ` + +`; +ICONS["album"] = ` + +`; +ICONS["plus-circle-outline"] = ` + +`; +ICONS["plus-circle"] = ` + `; ICONS["play-circle"] = ` diff --git a/app/js/lib/mpd.js b/app/js/lib/mpd.js index 7ced72c..83c946c 100644 --- a/app/js/lib/mpd.js +++ b/app/js/lib/mpd.js @@ -21,11 +21,13 @@ function onMessage(e) { function onError(e) { console.error(e); + current && current.reject(e); ws = null; // fixme } function onClose(e) { console.warn(e); + current && current.reject(e); ws = null; // fixme } diff --git a/app/js/player.js b/app/js/player.js index eabd60e..89ff349 100644 --- a/app/js/player.js +++ b/app/js/player.js @@ -10,9 +10,33 @@ const DOM = {}; let current = {}; let node; let idleTimeout = null; +let toggledVolume = 0; function sync(data) { - if ("volume" in data) { DOM.volume.value = data["volume"]; } + if ("volume" in data) { + data["volume"] = Number(data["volume"]); + + DOM.mute.disabled = false; + DOM.volume.disabled = false; + DOM.volume.value = data["volume"]; + + if (data["volume"] == 0 && current["volume"] > 0) { // muted + toggledVolume = current["volume"]; + html.clear(DOM.mute); + DOM.mute.appendChild(html.icon("volume-off")); + } + + if (data["volume"] > 0 && current["volume"] == 0) { // restored + toggledVolume = 0; + html.clear(DOM.mute); + DOM.mute.appendChild(html.icon("volume-high")); + } + + } else { + DOM.mute.disabled = true; + DOM.volume.disabled = true; + DOM.volume.value = 50; + } // changed time let elapsed = Number(data["elapsed"] || 0); @@ -22,13 +46,16 @@ function sync(data) { if (data["file"] != current["file"]) { // changed song if (data["file"]) { // playing at all? let duration = Number(data["duration"]); - DOM.duration.textContent = format.time(duration); + DOM.duration.textContent = format.time(duration); DOM.progress.max = duration; + DOM.progress.disabled = false; DOM.title.textContent = data["Title"] || data["file"].split("/").pop(); DOM.subtitle.textContent = format.subtitle(data, {duration:false}); } else { DOM.title.textContent = ""; DOM.subtitle.textContent = ""; + DOM.progress.value = 0; + DOM.progress.disabled = true; } pubsub.publish("song-change", null, data); @@ -83,10 +110,8 @@ export function init(n) { let all = node.querySelectorAll("[class]"); Array.from(all).forEach(node => DOM[node.className] = node); - DOM.progress = DOM.timeline.querySelector("progress"); - - DOM.volume.insertBefore(html.icon("volume-high"), DOM.volume.firstChild); - DOM.volume = DOM.volume.querySelector("meter"); + DOM.progress = DOM.timeline.querySelector("[type=range]"); + DOM.volume = DOM.volume.querySelector("[type=range]"); DOM.play.addEventListener("click", e => command("play")); DOM.pause.addEventListener("click", e => command("pause 1")); @@ -96,11 +121,10 @@ export function init(n) { DOM.random.addEventListener("click", e => command(`random ${current["random"] == "1" ? "0" : "1"}`)); DOM.repeat.addEventListener("click", e => command(`repeat ${current["repeat"] == "1" ? "0" : "1"}`)); - DOM.progress.addEventListener("click", e => { - let rect = e.target.getBoundingClientRect(); - let frac = (e.clientX - rect.left) / rect.width; - command(`seekcur ${frac * e.target.max}`); - }); + DOM.volume.addEventListener("input", e => command(`setvol ${e.target.valueAsNumber}`)); + DOM.progress.addEventListener("input", e => command(`seekcur ${e.target.valueAsNumber}`)); + + DOM.mute.addEventListener("click", e => command(`setvol ${toggledVolume}`)); update(); }