Improve Art-Net and sACN tables with TX/RX pairing and better styling

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Ian Gulliver
2026-01-29 12:00:32 -08:00
parent b5e8bda1c4
commit bad57914d3

View File

@@ -330,16 +330,16 @@
} }
.data-table { .data-table {
width: 100%;
border-collapse: collapse; border-collapse: collapse;
background: #222; background: #222;
border-radius: 8px; border-radius: 8px;
overflow: hidden; overflow: hidden;
font-size: 11px; font-size: 11px;
margin: 0 auto;
} }
.data-table th, .data-table td { .data-table th, .data-table td {
padding: 6px 10px; padding: 6px 20px;
text-align: left; text-align: left;
border-bottom: 1px solid #333; border-bottom: 1px solid #333;
} }
@@ -2621,15 +2621,35 @@
function renderArtnetTable() { function renderArtnetTable() {
const nodes = tableData.nodes || []; const nodes = tableData.nodes || [];
let rows = []; const txByUniverse = new Map();
const rxByUniverse = new Map();
nodes.forEach(node => { nodes.forEach(node => {
const name = getLabel(node); const name = getLabel(node);
(node.artnet_inputs || []).forEach(u => {
rows.push({ node: name, direction: 'Input', universe: u, universeStr: formatUniverse(u) });
});
(node.artnet_outputs || []).forEach(u => { (node.artnet_outputs || []).forEach(u => {
rows.push({ node: name, direction: 'Output', universe: u, universeStr: formatUniverse(u) }); if (!txByUniverse.has(u)) txByUniverse.set(u, []);
txByUniverse.get(u).push(name);
}); });
(node.artnet_inputs || []).forEach(u => {
if (!rxByUniverse.has(u)) rxByUniverse.set(u, []);
rxByUniverse.get(u).push(name);
});
});
const allUniverses = new Set([...txByUniverse.keys(), ...rxByUniverse.keys()]);
let rows = [];
allUniverses.forEach(u => {
const txNodes = txByUniverse.get(u) || [];
const rxNodes = rxByUniverse.get(u) || [];
const maxLen = Math.max(txNodes.length, rxNodes.length, 1);
for (let i = 0; i < maxLen; i++) {
rows.push({
universe: u,
universeStr: formatUniverse(u),
tx: txNodes[i] || '',
rx: rxNodes[i] || ''
});
}
}); });
if (tableSortColumn) { if (tableSortColumn) {
@@ -2637,16 +2657,16 @@
} }
let html = '<table class="data-table"><thead><tr>'; let html = '<table class="data-table"><thead><tr>';
html += '<th data-sort="node">Node</th>'; html += '<th data-sort="tx">TX</th>';
html += '<th data-sort="direction">Direction</th>';
html += '<th data-sort="universe">Universe</th>'; html += '<th data-sort="universe">Universe</th>';
html += '<th data-sort="rx">RX</th>';
html += '</tr></thead><tbody>'; html += '</tr></thead><tbody>';
rows.forEach(r => { rows.forEach(r => {
html += '<tr>'; html += '<tr>';
html += '<td>' + escapeHtml(r.node) + '</td>'; html += '<td>' + escapeHtml(r.tx) + '</td>';
html += '<td>' + r.direction + '</td>';
html += '<td>' + r.universeStr + '</td>'; html += '<td>' + r.universeStr + '</td>';
html += '<td>' + escapeHtml(r.rx) + '</td>';
html += '</tr>'; html += '</tr>';
}); });
@@ -2656,22 +2676,39 @@
function renderSacnTable() { function renderSacnTable() {
const nodes = tableData.nodes || []; const nodes = tableData.nodes || [];
let rows = []; const txByUniverse = new Map();
const rxByUniverse = new Map();
nodes.forEach(node => { nodes.forEach(node => {
const name = getLabel(node); const name = getLabel(node);
const inputs = []; (node.sacn_outputs || []).forEach(u => {
if (!txByUniverse.has(u)) txByUniverse.set(u, []);
txByUniverse.get(u).push(name);
});
(node.multicast_groups || []).forEach(g => { (node.multicast_groups || []).forEach(g => {
if (typeof g === 'string' && g.startsWith('sacn:')) { if (typeof g === 'string' && g.startsWith('sacn:')) {
const u = parseInt(g.substring(5), 10); const u = parseInt(g.substring(5), 10);
if (!isNaN(u)) inputs.push(u); if (!isNaN(u)) {
if (!rxByUniverse.has(u)) rxByUniverse.set(u, []);
rxByUniverse.get(u).push(name);
}
} }
}); });
inputs.forEach(u => { });
rows.push({ node: name, direction: 'Input', universe: u });
}); const allUniverses = new Set([...txByUniverse.keys(), ...rxByUniverse.keys()]);
(node.sacn_outputs || []).forEach(u => { let rows = [];
rows.push({ node: name, direction: 'Output', universe: u }); allUniverses.forEach(u => {
}); const txNodes = txByUniverse.get(u) || [];
const rxNodes = rxByUniverse.get(u) || [];
const maxLen = Math.max(txNodes.length, rxNodes.length, 1);
for (let i = 0; i < maxLen; i++) {
rows.push({
universe: u,
tx: txNodes[i] || '',
rx: rxNodes[i] || ''
});
}
}); });
if (tableSortColumn) { if (tableSortColumn) {
@@ -2679,16 +2716,16 @@
} }
let html = '<table class="data-table"><thead><tr>'; let html = '<table class="data-table"><thead><tr>';
html += '<th data-sort="node">Node</th>'; html += '<th data-sort="tx">TX</th>';
html += '<th data-sort="direction">Direction</th>';
html += '<th data-sort="universe">Universe</th>'; html += '<th data-sort="universe">Universe</th>';
html += '<th data-sort="rx">RX</th>';
html += '</tr></thead><tbody>'; html += '</tr></thead><tbody>';
rows.forEach(r => { rows.forEach(r => {
html += '<tr>'; html += '<tr>';
html += '<td>' + escapeHtml(r.node) + '</td>'; html += '<td>' + escapeHtml(r.tx) + '</td>';
html += '<td>' + r.direction + '</td>';
html += '<td class="numeric">' + r.universe + '</td>'; html += '<td class="numeric">' + r.universe + '</td>';
html += '<td>' + escapeHtml(r.rx) + '</td>';
html += '</tr>'; html += '</tr>';
}); });