From 6465ffceeef5c3df404972bf4eaa1b24e451a7c4 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Mon, 9 Nov 2026 20:41:29 +0100 Subject: feat: window rows get app icons and a close/move menu --- static/modules/windows.js | 102 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 83 insertions(+), 19 deletions(-) (limited to 'static/modules/windows.js') diff --git a/static/modules/windows.js b/static/modules/windows.js index 002bb5b..c981846 100644 --- a/static/modules/windows.js +++ b/static/modules/windows.js @@ -18,6 +18,68 @@ function tile(label, onclick) { return b; } +let pop = null; +function closePop() { + if (pop) { + pop.remove(); + pop = null; + } +} + +function openMenu(anchor, win, workspaces) { + closePop(); + pop = document.createElement("div"); + pop.className = "wpop"; + pop.innerHTML = ` +
+ +
Move to desktop
+
`; + pop.querySelector(".wpop-title").textContent = win.title || win.class; + pop.querySelector(".wpop-close").onclick = () => { + closePop(); + post("close", { address: win.address }); + }; + const targets = pop.querySelector(".wpop-ws"); + for (const n of workspaces ?? []) { + const b = document.createElement("button"); + b.textContent = String(n); + b.onclick = () => { + closePop(); + post("move", { address: win.address, n }); + }; + targets.append(b); + } + document.body.append(pop); + const r = anchor.getBoundingClientRect(); + const width = pop.offsetWidth; + const height = pop.offsetHeight; + pop.style.top = `${Math.min(r.bottom + 6, window.innerHeight - height - 8)}px`; + pop.style.left = `${Math.max(8, Math.min(r.left, window.innerWidth - width - 8))}px`; +} + +document.addEventListener("pointerdown", (e) => { + if (pop && !pop.contains(e.target) && !e.target.closest(".wmenu")) closePop(); +}); + +function appIcon(w) { + if (!w.icons?.length) return null; + const img = document.createElement("img"); + img.className = "wicon"; + img.alt = ""; + let i = 0; + const next = () => { + if (i >= w.icons.length) { + img.remove(); + return; + } + img.src = `/icons/${w.icons[i++]}.svg`; + }; + img.addEventListener("error", next); + next(); + return img; +} + export default { title: "Windows", tab: "Windows", @@ -40,25 +102,6 @@ export default { ws.append(b); } - const list = el.querySelector(".winlist"); - list.textContent = ""; - for (const w of (st.windows ?? []).slice(0, 8)) { - const row = document.createElement("div"); - row.className = "wrow"; - 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 }); - const close = document.createElement("button"); - close.className = "wclose"; - close.textContent = "×"; - close.setAttribute("aria-label", "Close window"); - close.onclick = () => post("close", { address: w.address }); - row.append(title, close); - list.append(row); - } - const launches = el.querySelector(".launches"); launches.textContent = ""; for (const item of st.launches ?? []) { @@ -78,5 +121,26 @@ export default { b.onclick = () => post("launch", { key: item.key }); launches.append(b); } + + const list = el.querySelector(".winlist"); + list.textContent = ""; + for (const w of (st.windows ?? []).slice(0, 12)) { + const row = document.createElement("div"); + row.className = "wrow"; + row.classList.toggle("on", Boolean(w.active)); + const icon = appIcon(w); + if (icon) row.append(icon); + const title = document.createElement("span"); + title.className = "wt"; + title.textContent = w.title || w.class; + title.onclick = () => post("focus", { address: w.address }); + const menu = document.createElement("button"); + menu.className = "wmenu"; + menu.textContent = "⋮"; + menu.setAttribute("aria-label", "Window actions"); + menu.onclick = () => openMenu(menu, w, st.workspaces); + row.append(title, menu); + list.append(row); + } }, }; -- cgit v1.2.3