diff --git a/app/app.css b/app/app.css index 1deaf11..8ddb565 100644 --- a/app/app.css +++ b/app/app.css @@ -38,6 +38,7 @@ button { border: none; line-height: 1; cursor: pointer; + flex-shrink: 0; } @font-face { font-family: 'Lato'; @@ -71,6 +72,9 @@ nav ul { display: flex; flex-direction: row; } +nav ul .icon { + margin-right: 4px; +} nav ul li { text-align: center; flex: 1 0 0; @@ -85,15 +89,7 @@ nav ul li.active { #player { display: flex; flex-direction: row; -} -#player .art img { - vertical-align: top; -} -#player .info { - flex-grow: 1; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + align-items: center; } #player:not([data-state=play]) .pause { display: none; @@ -105,7 +101,20 @@ nav ul li.active { #player:not([data-flags~=repeat]) .repeat { opacity: 0.5; } -#player .icon { +#player .art img { + vertical-align: top; +} +#player .info { + flex-grow: 1; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +#player .controls { + white-space: nowrap; + text-align: center; +} +#player .controls .icon { width: 64px; } #player .misc { @@ -135,7 +144,7 @@ nav ul li.active { padding: 0 4px; white-space: nowrap; } -.component .grid li h2 { +.component .grid h2 { flex-grow: 1; font-size: 100%; font-weight: normal; @@ -143,17 +152,17 @@ nav ul li.active { overflow: hidden; text-overflow: ellipsis; } -.component .grid li button { - flex-shrink: 0; -} -@media (pointer: coarse) { - .component .grid li button .icon { - width: 32px; - } +.component .grid h2 .icon { + margin-right: 4px; } .component .grid li:nth-child(odd) { background-color: #555; } +@media (pointer: coarse) { + .component .grid .icon { + width: 32px; + } +} #queue { height: 100%; display: flex; @@ -173,7 +182,7 @@ nav ul li.active { padding: 0 4px; white-space: nowrap; } -#queue .grid li h2 { +#queue .grid h2 { flex-grow: 1; font-size: 100%; font-weight: normal; @@ -181,19 +190,19 @@ nav ul li.active { overflow: hidden; text-overflow: ellipsis; } -#queue .grid li button { - flex-shrink: 0; -} -@media (pointer: coarse) { - #queue .grid li button .icon { - width: 32px; - } +#queue .grid h2 .icon { + margin-right: 4px; } #queue .grid li:nth-child(odd) { background-color: #555; } -#queue .current { - font-weight: bold; +@media (pointer: coarse) { + #queue .grid .icon { + width: 32px; + } +} +#queue .current * { + font-weight: bold !important; } #library { height: 100%; @@ -214,7 +223,7 @@ nav ul li.active { padding: 0 4px; white-space: nowrap; } -#library .grid li h2 { +#library .grid h2 { flex-grow: 1; font-size: 100%; font-weight: normal; @@ -222,17 +231,17 @@ nav ul li.active { overflow: hidden; text-overflow: ellipsis; } -#library .grid li button { - flex-shrink: 0; -} -@media (pointer: coarse) { - #library .grid li button .icon { - width: 32px; - } +#library .grid h2 .icon { + margin-right: 4px; } #library .grid li:nth-child(odd) { background-color: #555; } +@media (pointer: coarse) { + #library .grid .icon { + width: 32px; + } +} #fs { height: 100%; display: flex; @@ -252,7 +261,7 @@ nav ul li.active { padding: 0 4px; white-space: nowrap; } -#fs .grid li h2 { +#fs .grid h2 { flex-grow: 1; font-size: 100%; font-weight: normal; @@ -260,17 +269,17 @@ nav ul li.active { overflow: hidden; text-overflow: ellipsis; } -#fs .grid li button { - flex-shrink: 0; -} -@media (pointer: coarse) { - #fs .grid li button .icon { - width: 32px; - } +#fs .grid h2 .icon { + margin-right: 4px; } #fs .grid li:nth-child(odd) { background-color: #555; } +@media (pointer: coarse) { + #fs .grid .icon { + width: 32px; + } +} #playlists { height: 100%; display: flex; @@ -290,7 +299,7 @@ nav ul li.active { padding: 0 4px; white-space: nowrap; } -#playlists .grid li h2 { +#playlists .grid h2 { flex-grow: 1; font-size: 100%; font-weight: normal; @@ -298,14 +307,14 @@ nav ul li.active { overflow: hidden; text-overflow: ellipsis; } -#playlists .grid li button { - flex-shrink: 0; -} -@media (pointer: coarse) { - #playlists .grid li button .icon { - width: 32px; - } +#playlists .grid h2 .icon { + margin-right: 4px; } #playlists .grid li:nth-child(odd) { background-color: #555; } +@media (pointer: coarse) { + #playlists .grid .icon { + width: 32px; + } +} diff --git a/app/css/app.less b/app/css/app.less index 1f3eb26..8ad08f5 100644 --- a/app/css/app.less +++ b/app/css/app.less @@ -37,9 +37,9 @@ button { border: none; line-height: 1; cursor: pointer; + flex-shrink: 0; } - @import "font.less"; @import "icons.less"; @import "main.less"; diff --git a/app/css/component.less b/app/css/component.less index 523a60b..f5f99fd 100644 --- a/app/css/component.less +++ b/app/css/component.less @@ -18,27 +18,25 @@ align-items: center; padding: 0 4px; white-space: nowrap; + } - h2 { - flex-grow: 1; - font-size: 100%; - font-weight: normal; - margin: 0; - overflow: hidden; - text-overflow: ellipsis; - } + h2 { + flex-grow: 1; + font-size: 100%; + font-weight: normal; + margin: 0; + overflow: hidden; + text-overflow: ellipsis; - button { - flex-shrink: 0; - - @media (pointer: coarse) { - .icon { width: 32px; } - } - } + .icon { margin-right: 4px; } } li:nth-child(odd) { background-color: #555; } + + @media (pointer: coarse) { + .icon { width: 32px; } + } } } diff --git a/app/css/nav.less b/app/css/nav.less index 41c289c..920a488 100644 --- a/app/css/nav.less +++ b/app/css/nav.less @@ -5,6 +5,10 @@ nav ul { display: flex; flex-direction: row; + .icon { + margin-right: 4px; + } + li { text-align: center; flex: 1 0 0; diff --git a/app/css/player.less b/app/css/player.less index da1d729..6efee57 100644 --- a/app/css/player.less +++ b/app/css/player.less @@ -1,8 +1,14 @@ #player { display: flex; flex-direction: row; + align-items: center; + + &:not([data-state=play]) .pause { display: none; } + &[data-state=play] .play { display: none; } + &:not([data-flags~=random]) .random, &:not([data-flags~=repeat]) .repeat { opacity: 0.5; } .art img { vertical-align: top; } + .info { flex-grow: 1; white-space: nowrap; @@ -10,12 +16,10 @@ text-overflow: ellipsis; } - &:not([data-state=play]) .pause { display: none; } - &[data-state=play] .play { display: none; } - &:not([data-flags~=random]) .random, &:not([data-flags~=repeat]) .repeat { opacity: 0.5; } - - .icon { - width: 64px; + .controls { + white-space: nowrap; + text-align: center; + .icon { width: 64px; } } .misc { diff --git a/app/css/queue.less b/app/css/queue.less index ed581a4..f168575 100644 --- a/app/css/queue.less +++ b/app/css/queue.less @@ -1,5 +1,4 @@ #queue { .component; - - .current { font-weight: bold; } + .current * { font-weight: bold !important; } } diff --git a/app/icons/download.svg b/app/icons/download.svg new file mode 100644 index 0000000..67c9440 --- /dev/null +++ b/app/icons/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/folder.svg b/app/icons/folder.svg new file mode 100644 index 0000000..250600d --- /dev/null +++ b/app/icons/folder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/library-music.svg b/app/icons/library-music.svg new file mode 100644 index 0000000..3bb1055 --- /dev/null +++ b/app/icons/library-music.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/music.svg b/app/icons/music.svg new file mode 100644 index 0000000..36d700f --- /dev/null +++ b/app/icons/music.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/index.html b/app/index.html index 2ab716a..c74906f 100644 --- a/app/index.html +++ b/app/index.html @@ -14,22 +14,26 @@

- / - - - - +
+ + + + +
+ / +
+
- - + +
- - + +
@@ -45,20 +49,20 @@
- +
diff --git a/app/js/app.js b/app/js/app.js index 2955e2e..0e2b78f 100644 --- a/app/js/app.js +++ b/app/js/app.js @@ -1,6 +1,7 @@ import * as nav from "./nav.js"; import * as mpd from "./lib/mpd.js"; import * as player from "./player.js"; +import * as html from "./lib/html.js"; import * as queue from "./queue.js"; import * as library from "./library.js"; @@ -11,6 +12,8 @@ import * as yt from "./yt.js"; const components = { queue, library, fs, playlists, yt }; export function activate(what) { + location.hash = what; + for (let id in components) { let node = document.querySelector(`#${id}`); if (what == id) { @@ -23,7 +26,15 @@ export function activate(what) { nav.active(what); } +function initIcons() { + Array.from(document.querySelectorAll("[data-icon]")).forEach(node => { + let icon = html.icon(node.dataset.icon); + node.insertBefore(icon, node.firstChild); + }); +} + async function init() { + initIcons(); await mpd.init(); nav.init(document.querySelector("nav")); diff --git a/app/js/lib/icons.js b/app/js/lib/icons.js index 8d71c07..8f18c4a 100644 --- a/app/js/lib/icons.js +++ b/app/js/lib/icons.js @@ -1,4 +1,7 @@ let ICONS={}; +ICONS["library-music"] = ` + +`; ICONS["pause"] = ` `; @@ -47,9 +50,15 @@ ICONS["minus-circle-outline"] = ` `; +ICONS["music"] = ` + +`; ICONS["repeat"] = ` `; +ICONS["folder"] = ` + +`; ICONS["close-circle-outline"] = ` `; @@ -59,6 +68,9 @@ ICONS["pause-circle-outline"] = ` `; +ICONS["download"] = ` + +`; ICONS["play-circle"] = ` `; diff --git a/app/js/lib/ui.js b/app/js/lib/ui.js index d8f519e..0b47ea8 100644 --- a/app/js/lib/ui.js +++ b/app/js/lib/ui.js @@ -30,9 +30,7 @@ function fileName(data) { function formatTitle(ctx, data) { let tokens = []; switch (ctx) { - case CTX_FS: - return `🎵 ${fileName(data)}`; - break; + case CTX_FS: return fileName(data); break; case CTX_LIBRARY: data["Track"] && tokens.push(data["Track"].padStart(2, "0")); @@ -108,8 +106,11 @@ function addButton(type, what, parent) { export function song(ctx, data, parent) { let node = html.node("li", {}, "", parent); + let title = formatTitle(ctx, data); - html.node("h2", {}, title, node); + let h2 = html.node("h2", {}, "", node); + if (ctx == CTX_FS || ctx == CTX_LIBRARY) { html.icon("music", h2); } + html.text(title, h2); html.node("span", {className:"duration"}, format.time(Number(data["duration"])), node); @@ -135,8 +136,9 @@ export function song(ctx, data, parent) { export function group(ctx, label, urlOrFilter, parent) { let node = html.node("li", {}, "", parent); - if (ctx == CTX_FS) { label = `📁 ${label}`; } - html.node("h2", {}, label, node); + let h2 = html.node("h2", {}, "", node); + if (ctx == CTX_FS) { html.icon("folder", h2); } + html.text(label, h2); let type = (ctx == CTX_FS ? TYPE_URL : TYPE_FILTER); @@ -149,8 +151,9 @@ export function group(ctx, label, urlOrFilter, parent) { export function playlist(name, parent) { let node = html.node("li", {}, "", parent); - html.icon("playlist-music", node) - html.node("h2", {}, name, node); + let h2 = html.node("h2", {}, "", node); + html.icon("playlist-music", h2) + html.text(name, h2); playButton(TYPE_PLAYLIST, name, node); addButton(TYPE_PLAYLIST, name, node); diff --git a/app/js/player.js b/app/js/player.js index dfd1088..54328eb 100644 --- a/app/js/player.js +++ b/app/js/player.js @@ -16,7 +16,7 @@ function sync(data) { if (data["file"] != current["file"]) { // changed song DOM.duration.textContent = format.time(Number(data["duration"] || 0)); - DOM.title.textContent = data["Title"] || ""; + DOM.title.textContent = data["Title"] || data["file"].split("/").pop(); DOM["artist-album"].textContent = format.artistAlbum(data["Artist"], data["Album"]); pubsub.publish("song-change", null, data); } @@ -76,12 +76,5 @@ 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.play.appendChild(html.icon("play")); - DOM.pause.appendChild(html.icon("pause")); - DOM.prev.appendChild(html.icon("rewind")); - DOM.next.appendChild(html.icon("fast-forward")); - DOM.random.appendChild(html.icon("shuffle")); - DOM.repeat.appendChild(html.icon("repeat")); - update(); } diff --git a/app/js/queue.js b/app/js/queue.js index b8c06dc..f005676 100644 --- a/app/js/queue.js +++ b/app/js/queue.js @@ -47,16 +47,12 @@ export function init(n) { pubsub.subscribe("song-change", onSongChange); pubsub.subscribe("queue-change", onQueueChange); - let clear = node.querySelector(".clear"); - clear.appendChild(html.icon("close")); - clear.addEventListener("click", async e => { + node.querySelector(".clear").addEventListener("click", async e => { await mpd.command("clear"); syncQueue(); }); - let save = node.querySelector(".save"); - save.appendChild(html.icon("content-save")); - save.addEventListener("click", e => { + node.querySelector(".save").addEventListener("click", e => { let name = prompt("Save current queue as a playlist?", "name"); if (name === null) { return; } mpd.command(`save "${mpd.escape(name)}"`); diff --git a/app/js/yt.js b/app/js/yt.js index f7fb6f9..735791a 100644 --- a/app/js/yt.js +++ b/app/js/yt.js @@ -22,5 +22,5 @@ export async function activate() {} export function init(n) { node = n; - node.querySelector(".go").addEventListener("click", onClick); + let button = node.querySelector(".go").addEventListener("click", onClick); }