feat: variable hash size badge links to detail page, shows per-advert hash sizes

- Badge is now a link to the detail page with ?highlight=hashsize
- Detail page auto-scrolls to Recent Packets section
- Each advert shows its hash size badge (yellow if different from current)
- Detail page shows always-visible explanation banner (not hidden)
- Side pane badge links to detail page too
This commit is contained in:
you
2026-03-23 16:46:24 +00:00
parent 80ae87380d
commit 09dcae274f

View File

@@ -119,8 +119,8 @@
body.innerHTML = `
<div class="node-full-card" style="padding:12px 16px;margin-bottom:8px">
<div class="node-detail-name" style="font-size:20px">${escapeHtml(n.name || '(unnamed)')}</div>
<div style="margin:4px 0 6px"><span class="badge" style="background:${roleColor}20;color:${roleColor}">${n.role}</span> ${n.hash_size ? `<span class="badge" style="background:var(--nav-bg);color:var(--nav-text);font-family:var(--mono)">${n.public_key.slice(0, n.hash_size * 2).toUpperCase()}</span>` : ''} ${n.hash_size_inconsistent ? `<span class="badge" style="background:var(--status-yellow);color:#000;font-size:10px;cursor:help" onclick="var el=this.parentElement.querySelector('.hash-mismatch-info');if(el)el.hidden=!el.hidden">⚠️ variable hash size</span>` : ''} ${statusLabel}</div>
${n.hash_size_inconsistent ? `<div class="hash-mismatch-info" hidden style="font-size:11px;color:var(--text-muted);margin:-2px 0 6px;padding:6px 10px;background:var(--surface-2);border-radius:4px;border-left:3px solid var(--status-yellow)">This node has sent adverts with different hash sizes (<strong>${(n.hash_sizes_seen||[]).join('-byte, ')}-byte</strong>). Likely a firmware bug in MeshCore versions before 1.14.1. Update firmware to fix.</div>` : ''}
<div style="margin:4px 0 6px"><span class="badge" style="background:${roleColor}20;color:${roleColor}">${n.role}</span> ${n.hash_size ? `<span class="badge" style="background:var(--nav-bg);color:var(--nav-text);font-family:var(--mono)">${n.public_key.slice(0, n.hash_size * 2).toUpperCase()}</span>` : ''} ${n.hash_size_inconsistent ? `<a href="#/nodes/${encodeURIComponent(n.public_key)}?highlight=hashsize" class="badge" style="background:var(--status-yellow);color:#000;font-size:10px;cursor:pointer;text-decoration:none">⚠️ variable hash size</a>` : ''} ${statusLabel}</div>
${n.hash_size_inconsistent ? `<div style="font-size:11px;color:var(--text-muted);margin:-2px 0 6px;padding:6px 10px;background:var(--surface-2);border-radius:4px;border-left:3px solid var(--status-yellow)">Adverts show varying hash sizes (<strong>${(n.hash_sizes_seen||[]).join('-byte, ')}-byte</strong>). Likely a firmware bug — update to MeshCore 1.14.1+. See advert details below.</div>` : ''}
<div class="node-detail-key mono" style="font-size:11px;word-break:break-all;margin-bottom:6px">${n.public_key}</div>
<div>
<button class="btn-primary" id="copyUrlBtn" style="font-size:12px;padding:4px 10px">📋 Copy URL</button>
@@ -180,9 +180,17 @@
const snr = p.snr != null ? ` · SNR ${p.snr}dB` : '';
const rssi = p.rssi != null ? ` · RSSI ${p.rssi}dBm` : '';
const obsBadge = p.observation_count > 1 ? ` <span class="badge badge-obs" title="Seen ${p.observation_count} times">👁 ${p.observation_count}</span>` : '';
// Show hash size per advert if inconsistent
let hashSizeBadge = '';
if (n.hash_size_inconsistent && p.payload_type === 4 && p.raw_hex) {
const pb = parseInt(p.raw_hex.slice(2, 4), 16);
const hs = ((pb >> 6) & 0x3) + 1;
const isDefault = hs === n.hash_size;
hashSizeBadge = ` <span class="badge" style="background:${isDefault ? 'var(--surface-2)' : 'var(--status-yellow)'};color:${isDefault ? 'var(--text-muted)' : '#000'};font-size:9px;font-family:var(--mono)">${hs}B</span>`;
}
return `<div class="node-activity-item">
<span class="node-activity-time">${timeAgo(p.timestamp)}</span>
<span>${typeLabel}${detail}${obsBadge}${obs ? ' via ' + escapeHtml(obs) : ''}${snr}${rssi}</span>
<span>${typeLabel}${detail}${hashSizeBadge}${obsBadge}${obs ? ' via ' + escapeHtml(obs) : ''}${snr}${rssi}</span>
<a href="#/packets/${p.hash}" class="ch-analyze-link" style="margin-left:8px;font-size:0.8em">Analyze →</a>
</div>`;
}).join('') : '<div class="text-muted">No recent packets</div>'}
@@ -210,6 +218,12 @@
}).catch(() => {});
});
// Auto-scroll to adverts if highlight=hashsize
if (location.hash.includes('highlight=hashsize')) {
const recentSection = body.querySelector('.node-activity-list');
if (recentSection) setTimeout(() => recentSection.scrollIntoView({ behavior: 'smooth', block: 'start' }), 300);
}
// QR code for full-screen view
const qrFullEl = document.getElementById('nodeFullQrCode');
if (qrFullEl && typeof qrcode === 'function') {
@@ -504,11 +518,10 @@
panel.innerHTML = `
<div class="node-detail">
<div class="node-detail-name">${escapeHtml(n.name || '(unnamed)')}</div>
<div class="node-detail-role"><span class="badge" style="background:${roleColor}20;color:${roleColor}">${n.role}</span> ${n.hash_size ? `<span class="badge" style="background:var(--nav-bg);color:var(--nav-text);font-family:var(--mono)">${n.public_key.slice(0, n.hash_size * 2).toUpperCase()}</span>` : ''} ${n.hash_size_inconsistent ? `<span class="badge" style="background:var(--status-yellow);color:#000;font-size:10px;cursor:help" onclick="var el=this.closest('.node-detail').querySelector('.hash-mismatch-info');if(el)el.hidden=!el.hidden">⚠️ variable hash size</span>` : ''} ${statusLabel}
<div class="node-detail-role"><span class="badge" style="background:${roleColor}20;color:${roleColor}">${n.role}</span> ${n.hash_size ? `<span class="badge" style="background:var(--nav-bg);color:var(--nav-text);font-family:var(--mono)">${n.public_key.slice(0, n.hash_size * 2).toUpperCase()}</span>` : ''} ${n.hash_size_inconsistent ? `<a href="#/nodes/${encodeURIComponent(n.public_key)}?highlight=hashsize" class="badge" style="background:var(--status-yellow);color:#000;font-size:10px;cursor:pointer;text-decoration:none">⚠️ variable hash size</a>` : ''} ${statusLabel}
<a href="#/nodes/${encodeURIComponent(n.public_key)}" class="btn-primary" style="display:inline-block;text-decoration:none;font-size:11px;padding:2px 8px;margin-left:8px">🔍 Details</a>
<a href="#/nodes/${encodeURIComponent(n.public_key)}/analytics" class="btn-primary" style="display:inline-block;margin-left:4px;text-decoration:none;font-size:11px;padding:2px 8px">📊 Analytics</a>
</div>
${n.hash_size_inconsistent ? `<div class="hash-mismatch-info" hidden style="font-size:11px;color:var(--text-muted);margin:0 0 8px;padding:6px 10px;background:var(--surface-2);border-radius:4px;border-left:3px solid var(--status-yellow)">Adverts show hash sizes <strong>${(n.hash_sizes_seen||[]).join('-byte, ')}-byte</strong>. Likely a firmware bug — update to MeshCore 1.14.1+.</div>` : ''}
${hasLoc ? `<div class="node-map-qr-wrap">
<div class="node-map-container node-detail-map" id="nodeMap" style="border-radius:8px;overflow:hidden;"></div>