feat(ui,isotest): Group disabled displays into a collapsible section and refactor display item rendering; start a background screenshot loop during isotest and improve test-run cleanup
This commit is contained in:
@@ -1,5 +1,13 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-01-10 - 0.5.0 - feat(ui,isotest)
|
||||||
|
Group disabled displays into a collapsible section and refactor display item rendering; start a background screenshot loop during isotest and improve test-run cleanup
|
||||||
|
|
||||||
|
- Refactored display rendering: introduced renderDisplayItem() and simplified updateDisplaysUI() to separate enabled/disabled displays
|
||||||
|
- Disabled displays are collapsed under a <details> summary showing count ("Disabled Displays (N)")
|
||||||
|
- Added a background screenshot loop in isotest/run-test.sh that runs screenshot.sh every 5 seconds and records SCREENSHOT_LOOP_PID
|
||||||
|
- Improved cleanup in isotest/run-test.sh to kill SCREENSHOT_LOOP_PID and ENABLE_PID if they are running
|
||||||
|
|
||||||
## 2026-01-10 - 0.4.15 - fix(isotest)
|
## 2026-01-10 - 0.4.15 - fix(isotest)
|
||||||
Improve robustness of SPICE display enabler: add logging, wait-for-port and URI parsing, retries and reconnection logic, stabilization delay before configuring, and verification/retry of monitor configuration
|
Improve robustness of SPICE display enabler: add logging, wait-for-port and URI parsing, retries and reconnection logic, stabilization delay before configuring, and verification/retry of monitor configuration
|
||||||
|
|
||||||
|
|||||||
@@ -725,31 +725,48 @@ export class UIServer {
|
|||||||
setInterval(fetchUpdates, 60000); // Check every minute
|
setInterval(fetchUpdates, 60000); // Check every minute
|
||||||
|
|
||||||
// Display management
|
// Display management
|
||||||
|
function renderDisplayItem(d) {
|
||||||
|
return '<div class="device-item" style="flex-wrap: wrap; gap: 8px;">' +
|
||||||
|
'<div style="flex: 1; min-width: 150px;">' +
|
||||||
|
'<div class="device-name">' + d.name + '</div>' +
|
||||||
|
'<div style="font-size: 11px; color: var(--text-dim);">' +
|
||||||
|
d.width + 'x' + d.height + ' @ ' + d.refreshRate + 'Hz' +
|
||||||
|
(d.make !== 'Unknown' ? ' • ' + d.make : '') +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div style="display: flex; gap: 4px;">' +
|
||||||
|
(d.isPrimary
|
||||||
|
? '<span class="device-default">Primary</span>'
|
||||||
|
: (d.active ? '<button class="btn btn-primary" style="padding: 2px 8px; margin: 0; font-size: 11px;" onclick="setKioskDisplay(\\'' + d.name + '\\')">Set Primary</button>' : '')) +
|
||||||
|
'<button class="btn ' + (d.active ? 'btn-danger' : 'btn-primary') + '" style="padding: 2px 8px; margin: 0; font-size: 11px;" onclick="toggleDisplay(\\'' + d.name + '\\', ' + !d.active + ')">' +
|
||||||
|
(d.active ? 'Disable' : 'Enable') +
|
||||||
|
'</button>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>';
|
||||||
|
}
|
||||||
|
|
||||||
function updateDisplaysUI(data) {
|
function updateDisplaysUI(data) {
|
||||||
const list = document.getElementById('displays-list');
|
const list = document.getElementById('displays-list');
|
||||||
if (!data.displays || data.displays.length === 0) {
|
if (!data.displays || data.displays.length === 0) {
|
||||||
list.innerHTML = '<div style="color: var(--text-dim);">No displays detected</div>';
|
list.innerHTML = '<div style="color: var(--text-dim);">No displays detected</div>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
list.innerHTML = data.displays.map(d =>
|
|
||||||
'<div class="device-item" style="flex-wrap: wrap; gap: 8px;">' +
|
const enabled = data.displays.filter(d => d.active);
|
||||||
'<div style="flex: 1; min-width: 150px;">' +
|
const disabled = data.displays.filter(d => !d.active);
|
||||||
'<div class="device-name">' + d.name + '</div>' +
|
|
||||||
'<div style="font-size: 11px; color: var(--text-dim);">' +
|
let html = enabled.map(renderDisplayItem).join('');
|
||||||
d.width + 'x' + d.height + ' @ ' + d.refreshRate + 'Hz' +
|
|
||||||
(d.make !== 'Unknown' ? ' • ' + d.make : '') +
|
if (disabled.length > 0) {
|
||||||
'</div>' +
|
html += '<details style="margin-top: 12px;">' +
|
||||||
|
'<summary style="cursor: pointer; color: var(--text-dim); font-size: 12px; padding: 4px 0;">Disabled Displays (' + disabled.length + ')</summary>' +
|
||||||
|
'<div style="margin-top: 8px;">' +
|
||||||
|
disabled.map(renderDisplayItem).join('') +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'<div style="display: flex; gap: 4px;">' +
|
'</details>';
|
||||||
(d.isPrimary
|
}
|
||||||
? '<span class="device-default">Primary</span>'
|
|
||||||
: '<button class="btn btn-primary" style="padding: 2px 8px; margin: 0; font-size: 11px;" onclick="setKioskDisplay(\\'' + d.name + '\\')">Set Primary</button>') +
|
list.innerHTML = html;
|
||||||
'<button class="btn ' + (d.active ? 'btn-danger' : 'btn-primary') + '" style="padding: 2px 8px; margin: 0; font-size: 11px;" onclick="toggleDisplay(\\'' + d.name + '\\', ' + !d.active + ')">' +
|
|
||||||
(d.active ? 'Disable' : 'Enable') +
|
|
||||||
'</button>' +
|
|
||||||
'</div>' +
|
|
||||||
'</div>'
|
|
||||||
).join('');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchDisplays() {
|
function fetchDisplays() {
|
||||||
|
|||||||
@@ -63,6 +63,12 @@ fi
|
|||||||
cleanup() {
|
cleanup() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "Shutting down..."
|
echo "Shutting down..."
|
||||||
|
if [ -n "$SCREENSHOT_LOOP_PID" ] && kill -0 "$SCREENSHOT_LOOP_PID" 2>/dev/null; then
|
||||||
|
kill "$SCREENSHOT_LOOP_PID" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if [ -n "$ENABLE_PID" ] && kill -0 "$ENABLE_PID" 2>/dev/null; then
|
||||||
|
kill "$ENABLE_PID" 2>/dev/null || true
|
||||||
|
fi
|
||||||
if [ -n "$VIEWER_PID" ] && kill -0 "$VIEWER_PID" 2>/dev/null; then
|
if [ -n "$VIEWER_PID" ] && kill -0 "$VIEWER_PID" 2>/dev/null; then
|
||||||
kill "$VIEWER_PID" 2>/dev/null || true
|
kill "$VIEWER_PID" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
@@ -212,11 +218,15 @@ if [ -f "$SCRIPT_DIR/enable-displays.py" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Tips:"
|
echo "Tips:"
|
||||||
echo " - pnpm run test:screenshot - Take screenshot"
|
|
||||||
echo " - http://localhost:3006 - Management UI"
|
echo " - http://localhost:3006 - Management UI"
|
||||||
echo " - socat - UNIX-CONNECT:.nogit/vm/serial.sock - Serial console (login: ecouser/ecouser)"
|
echo " - socat - UNIX-CONNECT:.nogit/vm/serial.sock - Serial console (login: ecouser/ecouser)"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
# Start screenshot loop in background (takes screenshots every 5 seconds)
|
||||||
|
echo "Starting screenshot loop..."
|
||||||
|
(while true; do "$SCRIPT_DIR/screenshot.sh" 2>/dev/null; sleep 5; done) &
|
||||||
|
SCREENSHOT_LOOP_PID=$!
|
||||||
|
|
||||||
if [ "$AUTO_MODE" = true ]; then
|
if [ "$AUTO_MODE" = true ]; then
|
||||||
echo "=== Auto mode: waiting for display setup ==="
|
echo "=== Auto mode: waiting for display setup ==="
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user