diff options
| author | Danilo M. <danix@danix.xyz> | 2026-11-09 18:26:00 +0100 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-11-09 18:26:00 +0100 |
| commit | 46a164e797c0fd77ee67e2bae9bec5c02ad1de2d (patch) | |
| tree | 566bb56c30b85a37f15b96a529ff252d69acb867 /static/modules | |
| parent | 7f99167f0b6a3c83bf77e20bc5e03942b901fb71 (diff) | |
| download | deck-it-46a164e797c0fd77ee67e2bae9bec5c02ad1de2d.tar.gz deck-it-46a164e797c0fd77ee67e2bae9bec5c02ad1de2d.zip | |
fix: add stats, music and windows panels
Diffstat (limited to 'static/modules')
| -rw-r--r-- | static/modules/music.js | 48 | ||||
| -rw-r--r-- | static/modules/obs.js | 2 | ||||
| -rw-r--r-- | static/modules/stats.js | 49 | ||||
| -rw-r--r-- | static/modules/windows.js | 65 |
4 files changed, 163 insertions, 1 deletions
diff --git a/static/modules/music.js b/static/modules/music.js new file mode 100644 index 0000000..de9df7f --- /dev/null +++ b/static/modules/music.js @@ -0,0 +1,48 @@ +async function post(action) { + try { + await fetch(`/api/music/${action}`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: "{}", + }); + } catch { + // the next poll shows the real state + } +} + +function button(label, action) { + const b = document.createElement("button"); + b.textContent = label; + b.onclick = () => post(action); + return b; +} + +export default { + title: "Music", + interval: 2, + + mount(el) { + const box = document.createElement("div"); + box.className = "music"; + box.innerHTML = ` + <div class="now"><b class="title">—</b><span class="artist muted"></span></div> + <div class="row controls"></div> + <div class="row vol"></div>`; + box.querySelector(".controls").append( + button("⏮", "prev"), button("⏯", "play"), button("⏭", "next")); + box.querySelector(".vol").append( + button("Vol−", "voldown"), button("Mute", "mute"), button("Vol+", "volup")); + el.append(box); + }, + + update(st, el) { + const box = el.querySelector(".music"); + box.querySelector(".title").textContent = st.title || "Nothing playing"; + box.querySelector(".artist").textContent = st.artist || ""; + box.querySelector(".controls button:nth-child(2)").textContent = + st.status === "Playing" ? "⏸" : "▶"; + const mute = box.querySelector(".vol button:nth-child(2)"); + mute.classList.toggle("on", Boolean(st.volume?.muted)); + mute.textContent = st.volume?.muted ? "Unmute" : "Mute"; + }, +}; diff --git a/static/modules/obs.js b/static/modules/obs.js index c6a3ebc..9c5c34c 100644 --- a/static/modules/obs.js +++ b/static/modules/obs.js @@ -40,7 +40,7 @@ export default { const sources = el.querySelector(".obs-sources"); if (!state.connected) { - controls.textContent = "OBS offline"; + controls.textContent = state.error ? `OBS offline: ${state.error}` : "OBS offline"; scenes.textContent = audio.textContent = sources.textContent = ""; return; } diff --git a/static/modules/stats.js b/static/modules/stats.js new file mode 100644 index 0000000..b29b4ae --- /dev/null +++ b/static/modules/stats.js @@ -0,0 +1,49 @@ +const gb = (kb) => (kb / 1048576).toFixed(1); +const rate = (b) => (b >= 1048576 ? `${(b / 1048576).toFixed(1)} MB/s` : `${Math.round(b / 1024)} KB/s`); +const uptime = (s) => { + const d = Math.floor(s / 86400); + const h = Math.floor((s % 86400) / 3600); + const m = Math.floor((s % 3600) / 60); + return d ? `${d}d ${h}h` : `${h}h ${m}m`; +}; +const gpu = (g) => { + if (!g) return "—"; + if (g.asleep) return `${g.name} (asleep)`; + return `${g.name} ${g.util}%${g.temp != null ? ` · ${g.temp}°C` : ""}`; +}; + +function row(label, value) { + const div = document.createElement("div"); + div.className = "row"; + const name = document.createElement("span"); + name.className = "muted"; + name.textContent = label; + const val = document.createElement("span"); + val.className = "num"; + val.textContent = value; + div.append(name, val); + return div; +} + +export default { + title: "System", + interval: 1, + + mount(el) { + const box = document.createElement("div"); + box.className = "stats"; + el.append(box); + }, + + update(st, el) { + const box = el.querySelector(".stats"); + box.textContent = ""; + box.append( + row("CPU", `${st.cpu}%${st.cpuTemp != null ? ` · ${st.cpuTemp}°C` : ""}`), + row("Memory", `${gb(st.memUsed)} / ${gb(st.memTotal)} GiB`), + row("GPU", gpu(st.gpu)), + row("Network", `↓ ${rate(st.net.down)} ↑ ${rate(st.net.up)}`), + row("Uptime", uptime(st.uptime)), + ); + }, +}; diff --git a/static/modules/windows.js b/static/modules/windows.js new file mode 100644 index 0000000..7a2eeb8 --- /dev/null +++ b/static/modules/windows.js @@ -0,0 +1,65 @@ +async function post(action, body = {}) { + try { + await fetch(`/api/windows/${action}`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(body), + }); + } catch { + // the next poll shows the real state + } +} + +function button(label, onclick) { + const b = document.createElement("button"); + b.textContent = label; + b.onclick = onclick; + return b; +} + +export default { + title: "Windows", + interval: 1, + + mount(el) { + const box = document.createElement("div"); + box.className = "windows"; + box.innerHTML = ` + <div class="row ws"></div> + <div class="winlist"></div> + <div class="row launches"></div>`; + el.append(box); + }, + + update(st, el) { + const box = el.querySelector(".windows"); + + const ws = box.querySelector(".ws"); + ws.textContent = ""; + for (const id of st.workspaces ?? []) { + const b = button(String(id), () => post("workspace", { n: id })); + b.classList.toggle("on", (st.windows ?? []).some((w) => w.workspace === id)); + ws.append(b); + } + + const list = box.querySelector(".winlist"); + list.textContent = ""; + for (const w of (st.windows ?? []).slice(0, 12)) { + const row = document.createElement("div"); + row.className = "row"; + row.classList.toggle("on", Boolean(w.active)); + const title = document.createElement("span"); + title.className = "wt"; + title.textContent = w.title || w.class; + title.onclick = () => post("focus", { address: w.address }); + row.append(title, button("×", () => post("close", { address: w.address }))); + list.append(row); + } + + const launches = box.querySelector(".launches"); + launches.textContent = ""; + for (const key of st.launches ?? []) { + launches.append(button(key, () => post("launch", { key }))); + } + }, +}; |
