Release 1.5.13

This commit is contained in:
2026-07-26 20:26:01 +01:00
parent 4afaeaafb1
commit 815c808d73
59 changed files with 1418 additions and 3042 deletions
+69 -21
View File
@@ -180,18 +180,50 @@
].join('');
}
function renderScreenRow(screen) {
function renderScreenTile(screen) {
var clientCount = Number(screen.player_connection_count || 0);
var playerUrl = String(screen.player_url || '').trim();
var connectionLabel = clientCount ? clientCount + ' live' : 'No clients';
var connectionStateClass = clientCount ? 'is-live' : 'is-idle';
var playlistLabel = screen.playlist_name ? escapeHtml(screen.playlist_name) : 'Unassigned';
var connectionsLabel = clientCount ? clientCount + ' connected' : 'No clients connected';
return [
'<tr>',
'<td data-label="Name">' + escapeHtml(screen.name) + '</td>',
'<td data-label="Player URL"><a href="' + escapeHtml(screen.player_url || '') + '" target="_blank">' + escapeHtml(screen.player_url || '') + '</a></td>',
'<td data-label="Playlist">' + escapeHtml(screen.playlist_name || '') + '</td>',
'<td data-label="Connected clients">' + (clientCount ? '<div class="connection-count" data-screen-connection-count="' + escapeHtml(screen.slug) + '">' + clientCount + ' connected</div>' : '<span class="empty">No clients connected.</span>') + '</td>',
'</tr>'
'<article class="dashboard-screen-tile" data-screen-key="' + escapeHtml(screen.id || '') + '">',
'<div class="dashboard-screen-tile-top">',
'<div class="dashboard-screen-tile-text">',
'<h4 class="dashboard-screen-name">' + escapeHtml(screen.name || '') + '</h4>',
'<a class="dashboard-screen-link" href="' + escapeHtml(playerUrl) + '" target="_blank">' + escapeHtml(playerUrl) + '</a>',
'</div>',
'<span class="dashboard-screen-pill ' + connectionStateClass + '">' + escapeHtml(connectionLabel) + '</span>',
'</div>',
'<dl class="dashboard-screen-meta">',
'<div><dt>Playlist</dt><dd>' + playlistLabel + '</dd></div>',
'<div><dt>Connections</dt><dd>' + escapeHtml(connectionsLabel) + '</dd></div>',
'</dl>',
'</article>'
].join('');
}
function compareScreensByConnectedClients(left, right) {
var leftCount = Number(left && left.player_connection_count || 0);
var rightCount = Number(right && right.player_connection_count || 0);
if (leftCount !== rightCount) {
return rightCount - leftCount;
}
var leftName = String(left && left.name || '').trim();
var rightName = String(right && right.name || '').trim();
var nameCompare = leftName.localeCompare(rightName, undefined, { sensitivity: 'base', numeric: true });
if (nameCompare !== 0) {
return nameCompare;
}
return String(left && left.slug || '').localeCompare(String(right && right.slug || ''), undefined, { sensitivity: 'base', numeric: true });
}
function updateStats(state) {
var clientCount = document.getElementById('dashboard-client-count');
var screenCount = document.getElementById('dashboard-screen-count');
@@ -280,36 +312,52 @@
tbody.removeChild(tbody.lastElementChild);
}
window.applyTableSort(document.getElementById('dashboard-clients-table'));
}
function updateScreenTable(state) {
var table = document.getElementById('dashboard-screens-table');
if (!table || !Array.isArray(state.screens)) {
function updateScreenGrid(state) {
var grid = document.getElementById('dashboard-screens-grid');
if (!grid || !Array.isArray(state.screens)) {
return;
}
var tbody = table.tBodies && table.tBodies[0] ? table.tBodies[0] : null;
if (!tbody) {
var screens = state.screens.slice().sort(compareScreensByConnectedClients);
if (!screens.length) {
grid.innerHTML = '<div class="empty dashboard-screen-empty">No screens yet.</div>';
return;
}
if (!state.screens.length) {
tbody.innerHTML = '<tr><td colspan="4" class="empty">No screens yet.</td></tr>';
return;
}
tbody.innerHTML = state.screens.map(renderScreenRow).join('');
window.applyTableSort(table);
grid.innerHTML = screens.map(renderScreenTile).join('');
}
function updateDashboardQuickActions(state) {
var pauseButton = document.getElementById('dashboard-pause-all-button');
var blackoutButton = document.getElementById('dashboard-blackout-all-button');
if (!blackoutButton || !state || !Array.isArray(state.clients)) {
if ((!pauseButton && !blackoutButton) || !state || !Array.isArray(state.clients)) {
return;
}
var hasClients = state.clients.length > 0;
var allPaused = hasClients && state.clients.every(function (client) {
return Boolean(client && client.paused);
});
var allBlackout = hasClients && state.clients.every(function (client) {
return Boolean(client && client.blackout);
});
if (pauseButton) {
var pauseForm = pauseButton.form;
var pauseInput = pauseForm ? pauseForm.querySelector('input[name="paused"]') : null;
var pauseLabel = allPaused ? 'Resume all clients' : 'Pause all clients';
var pauseButtonIcon = allPaused ? 'bi-play-fill' : 'bi-pause-fill';
pauseButton.innerHTML = '<i class="bi ' + pauseButtonIcon + ' me-1" aria-hidden="true"></i>' + escapeHtml(pauseLabel);
setButtonVariant(pauseButton, ['btn-success', 'btn-secondary', 'btn-outline-secondary', 'btn-outline-dark'], allPaused ? 'btn-success' : 'btn-info');
if (pauseInput) {
pauseInput.value = allPaused ? 'false' : 'true';
}
if (pauseForm) {
pauseForm.setAttribute('data-confirm-message', allPaused ? 'Resume all connected clients?' : 'Pause all connected clients?');
}
pauseButton.setAttribute('aria-label', pauseLabel);
}
var label = allBlackout ? 'Restore all clients' : 'Blackout all clients';
var blackoutForm = blackoutButton.form;
var blackoutInput = blackoutForm ? blackoutForm.querySelector('input[name="blackout"]') : null;
@@ -331,7 +379,7 @@
return;
}
updateStats(state);
updateScreenTable(state);
updateScreenGrid(state);
updateClientTable(state);
updateDashboardQuickActions(state);
}