diff --git a/web/excel-filter.js b/web/excel-filter.js
index c39878f..00e843b 100644
--- a/web/excel-filter.js
+++ b/web/excel-filter.js
@@ -657,8 +657,10 @@
document.addEventListener('mousedown', e => {
if (menu && !menu.contains(e.target) && !(e.target.closest && e.target.closest('.xf-btn, .xf-chip, .col-filter-btn'))) closeMenu();
}, true);
- window.addEventListener('scroll', () => { if (menu && !menu.classList.contains('xf-sheet')) closeMenu(); }, { passive: true });
- window.addEventListener('resize', closeMenu);
+ // the card-field dialog stays open through scrolling and the phone keyboard resizing the page
+ const keepOpen = () => menuCtx && menuCtx.fields;
+ window.addEventListener('scroll', () => { if (menu && !keepOpen() && !menu.classList.contains('xf-sheet')) closeMenu(); }, { passive: true });
+ window.addEventListener('resize', () => { if (!keepOpen()) closeMenu(); });
// ------------------------------------------------------------------------------------
// Custom View card fields (phones/tablets): choose and order what each card shows
@@ -666,11 +668,14 @@
function openFieldEditor() {
closeMenu();
if (typeof customColumns === 'undefined') return;
- backdrop = el('div', 'xf-backdrop'); backdrop.onclick = closeMenu;
- menu = el('div', 'xf-menu xf-sheet xf-fields-sheet');
+ // Same frame as the device details dialog: under the nav, full width on phones
+ backdrop = el('div', 'modal-overlay xf-fields-overlay');
+ backdrop.style.display = 'block';
+ backdrop.onclick = e => { if (e.target === backdrop) closeMenu(); };
+ menu = el('div', 'modal-content xf-fields-sheet');
menuCtx = { fields: true };
+ backdrop.appendChild(menu);
document.body.appendChild(backdrop);
- document.body.appendChild(menu);
document.addEventListener('keydown', onMenuKey, true);
renderFieldEditor();
}
@@ -679,10 +684,10 @@
if (!menu) return;
const label = id => { try { return getCustomColDef(id).label; } catch (e) { return id; } };
menu.innerHTML = '';
- const head = el('div', 'xf-head');
- head.appendChild(el('span', 'xf-title', 'Card fields'));
- const x = el('button', 'xf-x', '×'); x.type = 'button'; x.onclick = closeMenu;
+ const head = el('div', 'modal-header');
+ const x = el('span', 'close-modal', '\u00d7'); x.title = 'Close'; x.onclick = closeMenu;
head.appendChild(x);
+ head.appendChild(el('h2', 'xf-fields-title', 'Card fields'));
menu.appendChild(head);
menu.appendChild(el('div', 'xf-hint', 'Choose what each card shows and in what order. The first field is the card title.'));
@@ -691,14 +696,15 @@
const row = el('div', 'xf-field');
row.appendChild(el('span', 'xf-field-name', label(id)));
if (i === 0) row.appendChild(el('span', 'xf-badge', 'Title'));
- const btn = (txt, title, fn, disabled) => {
- const b = el('button', 'xf-mini', txt); b.type = 'button'; b.title = title; b.disabled = !!disabled;
+ const btn = (svg, title, fn, disabled, cls) => {
+ const b = el('button', 'xf-mini' + (cls ? ' ' + cls : '')); b.type = 'button'; b.title = title; b.disabled = !!disabled;
+ b.innerHTML = svg; b.setAttribute('aria-label', title);
b.onclick = () => { fn(); renderFieldEditor(); };
row.appendChild(b);
};
- btn('↑', 'Move up', () => moveCustomColumn(i, i - 1), i === 0);
- btn('↓', 'Move down', () => moveCustomColumn(i, i + 1), i === customColumns.length - 1);
- btn('−', 'Remove from cards', () => toggleCustomColumn(id), customColumns.length <= 1);
+ btn('', 'Move up', () => moveCustomColumn(i, i - 1), i === 0);
+ btn('', 'Move down', () => moveCustomColumn(i, i + 1), i === customColumns.length - 1);
+ btn('', 'Remove from cards', () => toggleCustomColumn(id), customColumns.length <= 1, 'xf-mini-x');
shown.appendChild(row);
});
menu.appendChild(shown);
diff --git a/web/index.html b/web/index.html
index 20dc053..2683d59 100755
--- a/web/index.html
+++ b/web/index.html
@@ -2877,7 +2877,7 @@
detailsBody.style.minHeight = '0';
detailsBody.style.padding = '0';
} else {
- modalContent.style.maxWidth = '550px';
+ modalContent.style.maxWidth = '680px';
modalContent.style.width = '85%';
modalContent.style.height = 'auto';
modalContent.style.maxHeight = 'calc(100vh - 100px)';
diff --git a/web/layout.css b/web/layout.css
index 912bc6a..6ee274e 100644
--- a/web/layout.css
+++ b/web/layout.css
@@ -23,6 +23,10 @@ table.xf-table thead tr.filter-row { display: none !important; }
.master-table:not(#table-custom) thead { position: sticky; top: var(--sticky-top, 112px); z-index: 20; }
+/* A Workbench stack never needs to scroll sideways, and a sideways-scrolling box captures its
+ sticky header (which then sits over the device list on phones) */
+.stack-box.table-wrapper { overflow: visible !important; }
+
@media (min-width: 1101px) {
/* An overflowing wrapper would capture the sticky header; these tables never need to
scroll sideways at desktop widths (they're fixed-layout, 100% wide) */
@@ -223,9 +227,6 @@ body { padding-bottom: var(--dock-h, 40px) !important; }
.modal-header { padding: 14px 14px 8px; }
.modal-header h2 { font-size: 1.2em; word-break: break-all; }
- /* Details title: the Advanced/Simple View button gets its own line under the device
- name instead of floating over the content */
- #detailsTitle button { float: none !important; display: block; margin: 10px 0 0 !important; font-size: 0.65em !important; }
/* Device details: Advanced View's source / overview / destination columns stack,
and the whole dialog scrolls instead of each column scrolling separately */
@@ -332,7 +333,7 @@ table.xf-table thead th:has(> .xf-btn):not(.sortable-th) { position: relative; p
.xf-bar { display: none; }
@media (max-width: 1100px) {
.xf-bar {
- display: flex; gap: 6px; padding: 8px 10px; overflow-x: auto; scrollbar-width: none;
+ display: flex; flex-wrap: wrap; gap: 6px; padding: 8px 10px; overflow: visible; /* chips wrap: no sideways scrolling */
position: sticky; top: var(--sticky-top, 112px); z-index: 25;
background: linear-gradient(rgba(0,0,0,0.15), rgba(0,0,0,0.15)), var(--bg-card);
border-bottom: 1px solid var(--border-color); box-shadow: 0 4px 8px rgba(0,0,0,0.25);
@@ -360,6 +361,23 @@ table.xf-table thead th:has(> .xf-btn):not(.sortable-th) { position: relative; p
background: transparent; color: inherit; border: 1px solid var(--border-color); border-radius: 6px;
}
.xf-mini:disabled { opacity: 0.3; cursor: default; }
+.xf-mini { display: inline-flex; align-items: center; justify-content: center; padding: 0; }
+.xf-mini svg { width: 17px; height: 17px; }
+.xf-mini:hover:not(:disabled) { border-color: #3498db; color: #3498db; }
+.xf-mini.xf-mini-x:hover:not(:disabled) { border-color: #e74c3c; color: #e74c3c; }
+
+/* Card fields: the same dialog frame as the device details */
+.xf-fields-overlay .modal-content.xf-fields-sheet {
+ width: 85%; max-width: 550px; margin: 80px auto 20px; max-height: calc(100vh - 100px); max-height: calc(100dvh - 100px);
+ display: flex; flex-direction: column; overflow: hidden; font-size: 15px;
+}
+.xf-fields-sheet .modal-header { flex: none; }
+.xf-fields-sheet .xf-fields-title { margin: 0; }
+.xf-fields-sheet .xf-hint { padding: 12px 20px 8px; }
+.xf-fields-sheet .xf-fields { padding: 0 20px; }
+.xf-fields-sheet .xf-add { padding: 0 20px; }
+.xf-fields-sheet .xf-actions { padding: 12px 20px 16px; }
+.xf-fields-sheet .xf-actions button { flex: 1; padding: 11px; }
.xf-add { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; margin-top: 8px; }
.xf-fields-sheet .xf-list { flex: 1 1 auto; min-height: 110px; }
.xf-fields-sheet .xf-add .xf-search { flex: none; }
@@ -476,9 +494,10 @@ a.nav-brand:hover h2 { color: #d6eaf8; }
a.nav-brand:focus-visible { outline: 2px solid #3498db; outline-offset: 3px; }
/* Device details title: name on the left, Simple / Advanced View toggle on the right, one line */
-#detailsTitle { display: flex; align-items: center; gap: 12px; flex-wrap: nowrap; padding-right: 10px; min-width: 0; }
-/* the name gives way (ellipsis) so the toggle never drops to a second line */
-#detailsTitle .dt-name { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+/* The dialog is wide enough for name + toggle on one line; on a phone that can't fit both,
+ the toggle moves under the name rather than cutting the name off */
+#detailsTitle { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding-right: 10px; min-width: 0; }
+#detailsTitle .dt-name { flex: 0 1 auto; min-width: 0; white-space: nowrap; }
#detailsTitle .dt-name .copy-row-btn { vertical-align: middle; }
#detailsTitle .dt-toggle { flex: 0 0 auto; }
#detailsTitle .dt-toggle {
diff --git a/web/overview.js b/web/overview.js
index d4c6708..b5969b1 100644
--- a/web/overview.js
+++ b/web/overview.js
@@ -568,9 +568,14 @@
const td = (t, bold) => { const c = el('td'); if (bold) c.appendChild(el('b', null, t)); else c.textContent = t; c.title = t; return c; };
row.appendChild(td(r['Source Device'], true));
row.appendChild(td(r['Destination Device'] || 'N/A'));
- row.appendChild(td(statusOf(r)));
- row.appendChild(td(String(detail || '')));
+ row.appendChild(td(statusOf(r), true));
+ const dc = td(String(detail || ''));
+ // ping values colored like the USMT Status page
+ if (/^(Online|Timed Out|Not Found|DNS Issue)$/.test(String(detail || '')) && typeof getPingColor === 'function') { dc.style.color = getPingColor(detail); dc.style.fontWeight = 'bold'; }
+ row.appendChild(dc);
row.appendChild(td(r.Tags || 'None'));
+ // same row colors as the USMT Status page
+ try { const bg = themes[currentTheme].groupColors[r['Backup Status']]; if (bg) row.style.backgroundColor = bg; } catch (e) {}
row.onclick = () => showDeviceDetails(r['Source Device']);
tb.appendChild(row);
});
@@ -641,7 +646,7 @@
function signature() {
const a = g('apiData'), sc = g('softwareCache') || {}, docs = g('availableDocs') || [];
return [Array.isArray(a) ? a.length : 0, JSON.stringify(a || []).length, Object.keys(g('advancedDataCache') || {}).length,
- Object.keys(sc).length, docs.length, g('globalDataLastUpdated'), Math.floor(Date.now() / 300000)].join('|');
+ Object.keys(sc).length, docs.length, g('globalDataLastUpdated'), g('currentTheme'), Math.floor(Date.now() / 300000)].join('|');
}
function maybeRender(force) {
if (!visible()) return;