diff options
Diffstat (limited to 'static')
| -rw-r--r-- | static/deck.css | 23 | ||||
| -rw-r--r-- | static/modules/windows.js | 102 |
2 files changed, 105 insertions, 20 deletions
diff --git a/static/deck.css b/static/deck.css index c56a7e5..9aaeaec 100644 --- a/static/deck.css +++ b/static/deck.css @@ -263,9 +263,30 @@ select { } .wrow.on { color: var(--udt-accent); } .wt { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; } -.wclose { flex: none; width: 36px; height: 36px; padding: 0; } +.wicon { width: 24px; height: 24px; object-fit: contain; flex: none; } +.wmenu { flex: none; width: 40px; height: 40px; padding: 0; font-size: 18px; line-height: 1; } .windows .launches { display: flex; flex-wrap: wrap; gap: 8px; } +.wpop { + position: fixed; + z-index: 50; + min-width: 220px; + max-width: 80vw; + padding: 10px; + display: flex; + flex-direction: column; + gap: 8px; + background: var(--udt-mantle); + border: 1px solid var(--udt-surface1); + border-radius: 12px; + box-shadow: 0 12px 30px rgba(0, 0, 0, .35); +} +.wpop-title { font-weight: 600; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.wpop-label { font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(--udt-overlay0); } +.wpop-close { text-align: left; } +.wpop-ws { display: flex; flex-wrap: wrap; gap: 6px; } +.wpop-ws button { min-width: 44px; min-height: 44px; padding: 0; font-size: 16px; font-weight: 600; } + .llm { display: flex; flex-direction: column; gap: 10px; } .llm-sec { padding: 10px 12px; 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 = ` + <div class="wpop-title"></div> + <button class="wpop-close">Close window</button> + <div class="wpop-label">Move to desktop</div> + <div class="wpop-ws"></div>`; + 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); + } }, }; |
