Card layout for tables on phones and tablets; fit terminal bar

Sideways-scrolling tables only showed a few of the 11 columns at a time on
a phone or iPad. Below 1100px (where the nav already collapses) each row
now renders as a card: the device as the title and every other column as
a labelled line, one card per row on phones and two or more on tablets.

- web/mobile.js copies column names onto cells (data-label) for the cards
  and re-applies them as the dashboard re-renders; no-op on wide screens
- Column filters fold behind a "Show filters" button with an active count
- Custom View keeps its sort/filter header as a strip of chips
- Terminal: Follow and the "your actions (simulated)" legend move into the
  terminal window's title bar; tabs read "Server" / "Worker" on phones, so
  LIVE, the legend and Follow are all visible on small screens
This commit is contained in:
Johan Sandoval committed 2026-10-06 16:29:23 +00:00
1 parent 5a7fd4f2df
commit 2cbb03894b
5 files changed
+227 -24

No files matched your search

+14 -5
View File
@@ -89,11 +89,13 @@
</button>
<div class="usmt-term-tabs"></div>
<span class="usmt-term-live" title="Streaming the real PowerShell console output from the server"><i></i>LIVE</span>
${this.demo ? '<span class="usmt-term-legend" title="Lines marked with a dot were produced by your own actions. They are simulated in your browser and never reach the real services."><i class="usmt-term-legend-dot"></i>your actions (simulated)</span>' : ''}
<button class="usmt-term-btn usmt-term-follow active" type="button" title="Auto-scroll">Follow</button>
</div>
<div class="usmt-term-window">
<div class="usmt-term-titlebar"><span class="usmt-term-wtitle"></span></div>
<div class="usmt-term-titlebar">
<span class="usmt-term-wtitle"></span>
${this.demo ? '<span class="usmt-term-legend" title="Lines marked with a dot were produced by your own actions. They are simulated in your browser and never reach the real services."><i class="usmt-term-legend-dot"></i><span class="t-long">your actions&nbsp;</span>(simulated)</span>' : ''}
<button class="usmt-term-btn usmt-term-follow active" type="button" title="Keep scrolled to the newest output">Follow</button>
</div>
<div class="usmt-term-screens"></div>
</div>`;
document.body.appendChild(dock);
@@ -103,7 +105,11 @@
const screens = dock.querySelector('.usmt-term-screens');
TERMINALS.forEach(t => {
const b = document.createElement('button');
b.type = 'button'; b.className = 'usmt-term-tab'; b.dataset.id = t.id; b.textContent = t.title;
b.type = 'button'; b.className = 'usmt-term-tab'; b.dataset.id = t.id; b.title = t.title;
// "USMT-Server.ps1" on wide screens, just "Server" on phones
const m = t.title.match(/^(USMT-)(.+)(\.ps1)$/);
if (m) b.innerHTML = `<span class="t-long">${m[1]}</span>${m[2]}<span class="t-long">${m[3]}</span>`;
else b.textContent = t.title;
b.onclick = () => { this.select(t.id); if (!this.open) this.toggle(true); };
tabs.appendChild(b);
const body = document.createElement('div');
@@ -143,7 +149,10 @@
this.el.querySelectorAll('.usmt-term-tab').forEach(b => b.classList.toggle('active', b.dataset.id === id));
this.el.querySelectorAll('.usmt-term-body').forEach(b => b.classList.toggle('active', b.dataset.id === id));
const t = TERMINALS.find(x => x.id === id);
this.el.querySelector('.usmt-term-wtitle').textContent = (this.demo ? 'Administrator: PowerShell (demo container) - ' : 'Administrator: PowerShell - ') + t.title;
const wtitle = this.el.querySelector('.usmt-term-wtitle');
wtitle.innerHTML = '<span class="t-long"></span><span></span>';
wtitle.firstChild.textContent = this.demo ? 'Administrator: PowerShell (demo container) - ' : 'Administrator: PowerShell - ';
wtitle.lastChild.textContent = t.title;
this.el.querySelector(`.usmt-term-tab[data-id="${id}"]`).classList.remove('unread');
try { localStorage.setItem('usmt_term_tab', id); } catch (e) {}
this.poll(id);