Improve hover cards with wrapper pattern and consistent behavior
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
4
dante.go
4
dante.go
@@ -866,9 +866,9 @@ func (t *Tendrils) probeDanteDeviceWithPort(ip net.IP, port int) {
|
|||||||
if sub.TxChannelName != "" {
|
if sub.TxChannelName != "" {
|
||||||
typeStr := sub.ChannelType.String()
|
typeStr := sub.ChannelType.String()
|
||||||
if typeStr != "" {
|
if typeStr != "" {
|
||||||
channelInfo = fmt.Sprintf("%s->%02d:%s", sub.TxChannelName, sub.RxChannel, typeStr)
|
channelInfo = fmt.Sprintf("%s → %02d [%s]", sub.TxChannelName, sub.RxChannel, typeStr)
|
||||||
} else {
|
} else {
|
||||||
channelInfo = fmt.Sprintf("%s->%02d", sub.TxChannelName, sub.RxChannel)
|
channelInfo = fmt.Sprintf("%s → %02d", sub.TxChannelName, sub.RxChannel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sourceNode := t.nodes.GetOrCreateByName(txDeviceName)
|
sourceNode := t.nodes.GetOrCreateByName(txDeviceName)
|
||||||
|
|||||||
@@ -141,14 +141,18 @@
|
|||||||
border: 1px dashed #c9f;
|
border: 1px dashed #c9f;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node .switch-port .error-info,
|
.node .switch-port .link-stats-wrapper,
|
||||||
.node .uplink .error-info {
|
.node .uplink .link-stats-wrapper {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 100%;
|
bottom: 100%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
margin-bottom: 4px;
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node .switch-port .link-stats,
|
||||||
|
.node .uplink .link-stats {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
@@ -159,8 +163,7 @@
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.link-stats .lbl,
|
||||||
.error-info .lbl,
|
|
||||||
.node-info .lbl,
|
.node-info .lbl,
|
||||||
.dante-info .lbl,
|
.dante-info .lbl,
|
||||||
.dante-detail .lbl,
|
.dante-detail .lbl,
|
||||||
@@ -180,8 +183,8 @@
|
|||||||
width: 120px;
|
width: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node .switch-port:hover .error-info,
|
.node .switch-port:hover .link-stats-wrapper,
|
||||||
.node .uplink:hover .error-info {
|
.node .uplink:hover .link-stats-wrapper {
|
||||||
display: block;
|
display: block;
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
}
|
}
|
||||||
@@ -355,13 +358,16 @@
|
|||||||
background: #358;
|
background: #358;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node .dante-info .dante-detail {
|
.node .dante-info .dante-detail-wrapper {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 100%;
|
bottom: 100%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
margin-bottom: 4px;
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node .dante-info .dante-detail {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
@@ -372,7 +378,6 @@
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.node .dante-info::after {
|
.node .dante-info::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -387,8 +392,9 @@
|
|||||||
z-index: 100;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node .dante-info:hover .dante-detail {
|
.node .dante-info:hover .dante-detail-wrapper {
|
||||||
display: block;
|
display: block;
|
||||||
|
will-change: transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.dante-mode .node.dante-tx .dante-info,
|
body.dante-mode .node.dante-tx .dante-info,
|
||||||
@@ -405,11 +411,11 @@
|
|||||||
bottom: -8px;
|
bottom: -8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.dante-mode .node.dante-tx.dante-rx .dante-info.rx-info .dante-detail {
|
body.dante-mode .node.dante-tx.dante-rx .dante-info.rx-info .dante-detail-wrapper {
|
||||||
bottom: auto;
|
bottom: auto;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
margin-bottom: 0;
|
padding-bottom: 0;
|
||||||
margin-top: 4px;
|
padding-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -460,13 +466,16 @@
|
|||||||
background: #245;
|
background: #245;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node .artnet-info .artnet-detail {
|
.node .artnet-info .artnet-detail-wrapper {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 100%;
|
bottom: 100%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
margin-bottom: 4px;
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node .artnet-info .artnet-detail {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
@@ -477,7 +486,6 @@
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.node .artnet-info::after {
|
.node .artnet-info::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -492,8 +500,9 @@
|
|||||||
z-index: 100;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node .artnet-info:hover .artnet-detail {
|
.node .artnet-info:hover .artnet-detail-wrapper {
|
||||||
display: block;
|
display: block;
|
||||||
|
will-change: transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.artnet-mode .node.artnet-out .artnet-info,
|
body.artnet-mode .node.artnet-out .artnet-info,
|
||||||
@@ -510,11 +519,11 @@
|
|||||||
bottom: -8px;
|
bottom: -8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.artnet-mode .node.artnet-out.artnet-in .artnet-info.in-info .artnet-detail {
|
body.artnet-mode .node.artnet-out.artnet-in .artnet-info.in-info .artnet-detail-wrapper {
|
||||||
bottom: auto;
|
bottom: auto;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
margin-bottom: 0;
|
padding-bottom: 0;
|
||||||
margin-top: 4px;
|
padding-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -549,13 +558,16 @@
|
|||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node .sacn-info .sacn-detail {
|
.node .sacn-info .sacn-detail-wrapper {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 100%;
|
bottom: 100%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
margin-bottom: 4px;
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node .sacn-info .sacn-detail {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
@@ -566,7 +578,6 @@
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.node .sacn-info::after {
|
.node .sacn-info::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -581,8 +592,9 @@
|
|||||||
z-index: 100;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node .sacn-info:hover .sacn-detail {
|
.node .sacn-info:hover .sacn-detail-wrapper {
|
||||||
display: block;
|
display: block;
|
||||||
|
will-change: transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.sacn-mode .node.sacn-consumer .sacn-info {
|
body.sacn-mode .node.sacn-consumer .sacn-info {
|
||||||
@@ -610,56 +622,51 @@
|
|||||||
z-index: 100;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node .node-info {
|
.node .node-info-wrapper {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
margin-top: 4px;
|
padding-top: 8px;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node .node-info {
|
||||||
background: #333;
|
background: #333;
|
||||||
border: 1px solid #555;
|
border: 1px solid #555;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 6px 8px;
|
padding: 6px 8px;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
z-index: 1000;
|
|
||||||
text-align: left;
|
text-align: left;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node .node-info::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
bottom: 100%;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.node:hover .node-info {
|
.node:hover .node-info-wrapper {
|
||||||
display: block;
|
display: block;
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.dante-mode .node:not(.dante-tx):not(.dante-rx):hover .node-info {
|
body.dante-mode .node:not(.dante-tx):not(.dante-rx):hover .node-info-wrapper {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.artnet-mode .node:not(.artnet-out):not(.artnet-in):hover .node-info {
|
body.artnet-mode .node:not(.artnet-out):not(.artnet-in):hover .node-info-wrapper {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.sacn-mode .node:not(.sacn-consumer):hover .node-info {
|
body.sacn-mode .node:not(.sacn-consumer):hover .node-info-wrapper {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node:has(.switch-port:hover) .node-info,
|
.node:has(.switch-port:hover) .node-info-wrapper,
|
||||||
.node:has(.uplink:hover) .node-info,
|
.node:has(.uplink:hover) .node-info-wrapper,
|
||||||
.node:has(.dante-info:hover) .node-info,
|
.node:has(.dante-info:hover) .node-info-wrapper,
|
||||||
.node:has(.artnet-info:hover) .node-info,
|
.node:has(.artnet-info:hover) .node-info-wrapper,
|
||||||
.node:has(.sacn-info:hover) .node-info {
|
.node:has(.sacn-info:hover) .node-info-wrapper {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1137,18 +1144,28 @@
|
|||||||
if (speedClass) portEl.classList.add(speedClass);
|
if (speedClass) portEl.classList.add(speedClass);
|
||||||
const errIn = switchConnection.errors?.in || 0;
|
const errIn = switchConnection.errors?.in || 0;
|
||||||
const errOut = switchConnection.errors?.out || 0;
|
const errOut = switchConnection.errors?.out || 0;
|
||||||
|
const statsWrapper = document.createElement('div');
|
||||||
|
statsWrapper.className = 'link-stats-wrapper';
|
||||||
const statsInfo = document.createElement('div');
|
const statsInfo = document.createElement('div');
|
||||||
statsInfo.className = 'error-info';
|
statsInfo.className = 'link-stats';
|
||||||
let statsHtml = '<span class="lbl">LINK</span> ' + formatLinkSpeed(switchConnection.speed);
|
let statsHtml = '<span class="lbl">LINK</span> ' + formatLinkSpeed(switchConnection.speed);
|
||||||
statsHtml += '\n<span class="lbl">ERR</span> RX ' + errIn + ' / TX ' + errOut;
|
statsHtml += '\n<span class="lbl">ERR</span> RX ' + errIn + ' / TX ' + errOut;
|
||||||
|
let statsPlain = 'LINK: ' + formatLinkSpeed(switchConnection.speed);
|
||||||
|
statsPlain += '\nERR: RX ' + errIn + ' / TX ' + errOut;
|
||||||
if (switchConnection.rates) {
|
if (switchConnection.rates) {
|
||||||
const r = switchConnection.rates;
|
const r = switchConnection.rates;
|
||||||
statsHtml += '\n<span class="lbl">RX</span> ' + formatMbps(r.outBytes) + ' (' + formatPps(r.outPkts) + ')';
|
statsHtml += '\n<span class="lbl">RX</span> ' + formatMbps(r.outBytes) + ' (' + formatPps(r.outPkts) + ')';
|
||||||
statsHtml += '\n<span class="lbl">TX</span> ' + formatMbps(r.inBytes) + ' (' + formatPps(r.inPkts) + ')';
|
statsHtml += '\n<span class="lbl">TX</span> ' + formatMbps(r.inBytes) + ' (' + formatPps(r.inPkts) + ')';
|
||||||
|
statsPlain += '\nRX: ' + formatMbps(r.outBytes) + ' (' + formatPps(r.outPkts) + ')';
|
||||||
|
statsPlain += '\nTX: ' + formatMbps(r.inBytes) + ' (' + formatPps(r.inPkts) + ')';
|
||||||
}
|
}
|
||||||
statsInfo.innerHTML = statsHtml;
|
statsInfo.innerHTML = statsHtml;
|
||||||
statsInfo.addEventListener('click', (e) => e.stopPropagation());
|
statsInfo.addEventListener('click', (e) => {
|
||||||
portEl.appendChild(statsInfo);
|
e.stopPropagation();
|
||||||
|
navigator.clipboard.writeText(statsPlain);
|
||||||
|
});
|
||||||
|
statsWrapper.appendChild(statsInfo);
|
||||||
|
portEl.appendChild(statsWrapper);
|
||||||
div.appendChild(portEl);
|
div.appendChild(portEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1156,6 +1173,8 @@
|
|||||||
labelEl.textContent = getLabel(node);
|
labelEl.textContent = getLabel(node);
|
||||||
div.appendChild(labelEl);
|
div.appendChild(labelEl);
|
||||||
|
|
||||||
|
const nodeInfoWrapper = document.createElement('div');
|
||||||
|
nodeInfoWrapper.className = 'node-info-wrapper';
|
||||||
const nodeInfo = document.createElement('div');
|
const nodeInfo = document.createElement('div');
|
||||||
nodeInfo.className = 'node-info';
|
nodeInfo.className = 'node-info';
|
||||||
if (node.interfaces) {
|
if (node.interfaces) {
|
||||||
@@ -1186,7 +1205,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (nodeInfo.textContent) {
|
if (nodeInfo.textContent) {
|
||||||
div.appendChild(nodeInfo);
|
nodeInfoWrapper.appendChild(nodeInfo);
|
||||||
|
div.appendChild(nodeInfoWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSwitch(node) && uplinkInfo === 'ROOT') {
|
if (isSwitch(node) && uplinkInfo === 'ROOT') {
|
||||||
@@ -1202,18 +1222,28 @@
|
|||||||
if (speedClass) uplinkEl.classList.add(speedClass);
|
if (speedClass) uplinkEl.classList.add(speedClass);
|
||||||
const errIn = uplinkInfo.errors?.in || 0;
|
const errIn = uplinkInfo.errors?.in || 0;
|
||||||
const errOut = uplinkInfo.errors?.out || 0;
|
const errOut = uplinkInfo.errors?.out || 0;
|
||||||
|
const statsWrapper = document.createElement('div');
|
||||||
|
statsWrapper.className = 'link-stats-wrapper';
|
||||||
const statsInfo = document.createElement('div');
|
const statsInfo = document.createElement('div');
|
||||||
statsInfo.className = 'error-info';
|
statsInfo.className = 'link-stats';
|
||||||
let statsHtml = '<span class="lbl">LINK</span> ' + formatLinkSpeed(uplinkInfo.speed);
|
let statsHtml = '<span class="lbl">LINK</span> ' + formatLinkSpeed(uplinkInfo.speed);
|
||||||
statsHtml += '\n<span class="lbl">ERR</span> RX ' + errIn + ' / TX ' + errOut;
|
statsHtml += '\n<span class="lbl">ERR</span> RX ' + errIn + ' / TX ' + errOut;
|
||||||
|
let statsPlain = 'LINK: ' + formatLinkSpeed(uplinkInfo.speed);
|
||||||
|
statsPlain += '\nERR: RX ' + errIn + ' / TX ' + errOut;
|
||||||
if (uplinkInfo.rates) {
|
if (uplinkInfo.rates) {
|
||||||
const r = uplinkInfo.rates;
|
const r = uplinkInfo.rates;
|
||||||
statsHtml += '\n<span class="lbl">RX</span> ' + formatMbps(r.inBytes) + ' (' + formatPps(r.inPkts) + ')';
|
statsHtml += '\n<span class="lbl">RX</span> ' + formatMbps(r.inBytes) + ' (' + formatPps(r.inPkts) + ')';
|
||||||
statsHtml += '\n<span class="lbl">TX</span> ' + formatMbps(r.outBytes) + ' (' + formatPps(r.outPkts) + ')';
|
statsHtml += '\n<span class="lbl">TX</span> ' + formatMbps(r.outBytes) + ' (' + formatPps(r.outPkts) + ')';
|
||||||
|
statsPlain += '\nRX: ' + formatMbps(r.inBytes) + ' (' + formatPps(r.inPkts) + ')';
|
||||||
|
statsPlain += '\nTX: ' + formatMbps(r.outBytes) + ' (' + formatPps(r.outPkts) + ')';
|
||||||
}
|
}
|
||||||
statsInfo.innerHTML = statsHtml;
|
statsInfo.innerHTML = statsHtml;
|
||||||
statsInfo.addEventListener('click', (e) => e.stopPropagation());
|
statsInfo.addEventListener('click', (e) => {
|
||||||
uplinkEl.appendChild(statsInfo);
|
e.stopPropagation();
|
||||||
|
navigator.clipboard.writeText(statsPlain);
|
||||||
|
});
|
||||||
|
statsWrapper.appendChild(statsInfo);
|
||||||
|
uplinkEl.appendChild(statsWrapper);
|
||||||
div.appendChild(uplinkEl);
|
div.appendChild(uplinkEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1221,7 +1251,10 @@
|
|||||||
const txEl = document.createElement('div');
|
const txEl = document.createElement('div');
|
||||||
txEl.className = 'dante-info tx-info';
|
txEl.className = 'dante-info tx-info';
|
||||||
const firstDest = danteInfo.txTo[0].split('\n')[0];
|
const firstDest = danteInfo.txTo[0].split('\n')[0];
|
||||||
txEl.innerHTML = '<span class="lbl">→</span> ' + firstDest;
|
const txMore = danteInfo.txTo.length > 1 ? ', ...' : '';
|
||||||
|
txEl.innerHTML = '<span class="lbl">→</span> ' + firstDest + txMore;
|
||||||
|
const detailWrapper = document.createElement('div');
|
||||||
|
detailWrapper.className = 'dante-detail-wrapper';
|
||||||
const detail = document.createElement('div');
|
const detail = document.createElement('div');
|
||||||
detail.className = 'dante-detail';
|
detail.className = 'dante-detail';
|
||||||
const txHtml = danteInfo.txTo.map(entry => {
|
const txHtml = danteInfo.txTo.map(entry => {
|
||||||
@@ -1233,9 +1266,22 @@
|
|||||||
return '<span class="lbl">→</span> ' + line;
|
return '<span class="lbl">→</span> ' + line;
|
||||||
}).join('\n');
|
}).join('\n');
|
||||||
}).join('\n\n');
|
}).join('\n\n');
|
||||||
|
const txPlain = danteInfo.txTo.map(entry => {
|
||||||
|
const lines = entry.split('\n');
|
||||||
|
return lines.map(line => {
|
||||||
|
if (line.startsWith(' ')) {
|
||||||
|
return ' ' + line.trim();
|
||||||
|
}
|
||||||
|
return '→ ' + line;
|
||||||
|
}).join('\n');
|
||||||
|
}).join('\n\n');
|
||||||
detail.innerHTML = txHtml;
|
detail.innerHTML = txHtml;
|
||||||
detail.addEventListener('click', (e) => e.stopPropagation());
|
detail.addEventListener('click', (e) => {
|
||||||
txEl.appendChild(detail);
|
e.stopPropagation();
|
||||||
|
navigator.clipboard.writeText(txPlain);
|
||||||
|
});
|
||||||
|
detailWrapper.appendChild(detail);
|
||||||
|
txEl.appendChild(detailWrapper);
|
||||||
div.appendChild(txEl);
|
div.appendChild(txEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1243,7 +1289,10 @@
|
|||||||
const rxEl = document.createElement('div');
|
const rxEl = document.createElement('div');
|
||||||
rxEl.className = 'dante-info rx-info';
|
rxEl.className = 'dante-info rx-info';
|
||||||
const firstSource = danteInfo.rxFrom[0].split('\n')[0];
|
const firstSource = danteInfo.rxFrom[0].split('\n')[0];
|
||||||
rxEl.innerHTML = '<span class="lbl">←</span> ' + firstSource;
|
const rxMore = danteInfo.rxFrom.length > 1 ? ', ...' : '';
|
||||||
|
rxEl.innerHTML = '<span class="lbl">←</span> ' + firstSource + rxMore;
|
||||||
|
const detailWrapper = document.createElement('div');
|
||||||
|
detailWrapper.className = 'dante-detail-wrapper';
|
||||||
const detail = document.createElement('div');
|
const detail = document.createElement('div');
|
||||||
detail.className = 'dante-detail';
|
detail.className = 'dante-detail';
|
||||||
const rxHtml = danteInfo.rxFrom.map(entry => {
|
const rxHtml = danteInfo.rxFrom.map(entry => {
|
||||||
@@ -1255,45 +1304,82 @@
|
|||||||
return '<span class="lbl">←</span> ' + line;
|
return '<span class="lbl">←</span> ' + line;
|
||||||
}).join('\n');
|
}).join('\n');
|
||||||
}).join('\n\n');
|
}).join('\n\n');
|
||||||
|
const rxPlain = danteInfo.rxFrom.map(entry => {
|
||||||
|
const lines = entry.split('\n');
|
||||||
|
return lines.map(line => {
|
||||||
|
if (line.startsWith(' ')) {
|
||||||
|
return ' ' + line.trim();
|
||||||
|
}
|
||||||
|
return '← ' + line;
|
||||||
|
}).join('\n');
|
||||||
|
}).join('\n\n');
|
||||||
detail.innerHTML = rxHtml;
|
detail.innerHTML = rxHtml;
|
||||||
detail.addEventListener('click', (e) => e.stopPropagation());
|
detail.addEventListener('click', (e) => {
|
||||||
rxEl.appendChild(detail);
|
e.stopPropagation();
|
||||||
|
navigator.clipboard.writeText(rxPlain);
|
||||||
|
});
|
||||||
|
detailWrapper.appendChild(detail);
|
||||||
|
rxEl.appendChild(detailWrapper);
|
||||||
div.appendChild(rxEl);
|
div.appendChild(rxEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (artnetInfo && artnetInfo.isOut) {
|
if (artnetInfo && artnetInfo.isOut) {
|
||||||
const outEl = document.createElement('div');
|
const outEl = document.createElement('div');
|
||||||
outEl.className = 'artnet-info out-info';
|
outEl.className = 'artnet-info out-info';
|
||||||
outEl.innerHTML = '<span class="lbl">←</span> ' + artnetInfo.outputs[0];
|
const outMore = artnetInfo.outputs.length > 1 ? ', ...' : '';
|
||||||
|
outEl.innerHTML = '<span class="lbl">←</span> ' + artnetInfo.outputs[0] + outMore;
|
||||||
|
const detailWrapper = document.createElement('div');
|
||||||
|
detailWrapper.className = 'artnet-detail-wrapper';
|
||||||
const detail = document.createElement('div');
|
const detail = document.createElement('div');
|
||||||
detail.className = 'artnet-detail';
|
detail.className = 'artnet-detail';
|
||||||
detail.innerHTML = artnetInfo.outputs.map(u => '<span class="lbl">←</span> ' + u).join('\n');
|
detail.innerHTML = artnetInfo.outputs.map(u => '<span class="lbl">←</span> ' + u).join('\n');
|
||||||
detail.addEventListener('click', (e) => e.stopPropagation());
|
const outPlain = artnetInfo.outputs.map(u => '← ' + u).join('\n');
|
||||||
outEl.appendChild(detail);
|
detail.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
navigator.clipboard.writeText(outPlain);
|
||||||
|
});
|
||||||
|
detailWrapper.appendChild(detail);
|
||||||
|
outEl.appendChild(detailWrapper);
|
||||||
div.appendChild(outEl);
|
div.appendChild(outEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (artnetInfo && artnetInfo.isIn) {
|
if (artnetInfo && artnetInfo.isIn) {
|
||||||
const inEl = document.createElement('div');
|
const inEl = document.createElement('div');
|
||||||
inEl.className = 'artnet-info in-info';
|
inEl.className = 'artnet-info in-info';
|
||||||
inEl.innerHTML = '<span class="lbl">→</span> ' + artnetInfo.inputs[0];
|
const inMore = artnetInfo.inputs.length > 1 ? ', ...' : '';
|
||||||
|
inEl.innerHTML = '<span class="lbl">→</span> ' + artnetInfo.inputs[0] + inMore;
|
||||||
|
const detailWrapper = document.createElement('div');
|
||||||
|
detailWrapper.className = 'artnet-detail-wrapper';
|
||||||
const detail = document.createElement('div');
|
const detail = document.createElement('div');
|
||||||
detail.className = 'artnet-detail';
|
detail.className = 'artnet-detail';
|
||||||
detail.innerHTML = artnetInfo.inputs.map(u => '<span class="lbl">→</span> ' + u).join('\n');
|
detail.innerHTML = artnetInfo.inputs.map(u => '<span class="lbl">→</span> ' + u).join('\n');
|
||||||
detail.addEventListener('click', (e) => e.stopPropagation());
|
const inPlain = artnetInfo.inputs.map(u => '→ ' + u).join('\n');
|
||||||
inEl.appendChild(detail);
|
detail.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
navigator.clipboard.writeText(inPlain);
|
||||||
|
});
|
||||||
|
detailWrapper.appendChild(detail);
|
||||||
|
inEl.appendChild(detailWrapper);
|
||||||
div.appendChild(inEl);
|
div.appendChild(inEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sacnInfo && sacnInfo.isConsumer) {
|
if (sacnInfo && sacnInfo.isConsumer) {
|
||||||
const sacnEl = document.createElement('div');
|
const sacnEl = document.createElement('div');
|
||||||
sacnEl.className = 'sacn-info';
|
sacnEl.className = 'sacn-info';
|
||||||
sacnEl.innerHTML = '<span class="lbl">←</span> ' + sacnInfo.universes[0];
|
const sacnMore = sacnInfo.universes.length > 1 ? ', ...' : '';
|
||||||
|
sacnEl.innerHTML = '<span class="lbl">←</span> ' + sacnInfo.universes[0] + sacnMore;
|
||||||
|
const detailWrapper = document.createElement('div');
|
||||||
|
detailWrapper.className = 'sacn-detail-wrapper';
|
||||||
const detail = document.createElement('div');
|
const detail = document.createElement('div');
|
||||||
detail.className = 'sacn-detail';
|
detail.className = 'sacn-detail';
|
||||||
detail.innerHTML = sacnInfo.universes.map(u => '<span class="lbl">←</span> ' + u).join('\n');
|
detail.innerHTML = sacnInfo.universes.map(u => '<span class="lbl">←</span> ' + u).join('\n');
|
||||||
detail.addEventListener('click', (e) => e.stopPropagation());
|
const sacnPlain = sacnInfo.universes.map(u => '← ' + u).join('\n');
|
||||||
sacnEl.appendChild(detail);
|
detail.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
navigator.clipboard.writeText(sacnPlain);
|
||||||
|
});
|
||||||
|
detailWrapper.appendChild(detail);
|
||||||
|
sacnEl.appendChild(detailWrapper);
|
||||||
div.appendChild(sacnEl);
|
div.appendChild(sacnEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1644,7 +1730,7 @@
|
|||||||
const net = (u >> 8) & 0x7f;
|
const net = (u >> 8) & 0x7f;
|
||||||
const subnet = (u >> 4) & 0x0f;
|
const subnet = (u >> 4) & 0x0f;
|
||||||
const universe = u & 0x0f;
|
const universe = u & 0x0f;
|
||||||
return net + ':' + subnet + ':' + universe;
|
return net + ':' + subnet + ':' + universe + ' (' + u + ')';
|
||||||
};
|
};
|
||||||
|
|
||||||
const inputs = (an.inputs || []).map(formatUniverse);
|
const inputs = (an.inputs || []).map(formatUniverse);
|
||||||
|
|||||||
Reference in New Issue
Block a user