diff --git a/public/analytics.js b/public/analytics.js
index 576ec91..ec221c2 100644
--- a/public/analytics.js
+++ b/public/analytics.js
@@ -88,6 +88,20 @@
renderTab(btn.dataset.tab);
});
+ // Delegated click/keyboard handler for clickable table rows
+ const analyticsContent = document.getElementById('analyticsContent');
+ if (analyticsContent) {
+ const handler = (e) => {
+ const row = e.target.closest('tr[data-action="navigate"]');
+ if (!row) return;
+ if (e.type === 'keydown' && e.key !== 'Enter' && e.key !== ' ') return;
+ if (e.type === 'keydown') e.preventDefault();
+ location.hash = row.dataset.value;
+ };
+ analyticsContent.addEventListener('click', handler);
+ analyticsContent.addEventListener('keydown', handler);
+ }
+
try {
window._analyticsData = {};
const [hashData, rfData, topoData, chanData] = await Promise.all([
@@ -568,7 +582,7 @@
| Channel | Hash | Messages | Unique Senders | Last Activity | Decrypted |
- ${ch.channels.map(c => `
+ ${ch.channels.map(c => `
| ${esc(c.name || 'Unknown')} |
${c.hash} |
${c.messages} |
@@ -679,7 +693,7 @@
| Node | Hash Size | Adverts | Last Seen |
- ${data.multiByteNodes.map(n => `
+ ${data.multiByteNodes.map(n => `
| ${esc(n.name)} |
${n.hashSize}-byte |
${n.packets} |
@@ -697,7 +711,7 @@
${data.topHops.map(h => {
const link = h.pubkey ? `#/nodes/${encodeURIComponent(h.pubkey)}` : `#/packets?search=${h.hex}`;
- return `
+ return `
| ${h.hex} |
${h.name ? `${esc(h.name)}` : 'unknown'} |
${h.size}-byte |
diff --git a/public/nodes.js b/public/nodes.js
index 50ed95b..2dfde6d 100644
--- a/public/nodes.js
+++ b/public/nodes.js
@@ -257,6 +257,20 @@
th.addEventListener('click', () => { sortBy = th.dataset.sort; loadNodes(); });
});
+ // Delegated click/keyboard handler for table rows
+ const tbody = document.getElementById('nodesBody');
+ if (tbody) {
+ const handler = (e) => {
+ const row = e.target.closest('tr[data-action="select"]');
+ if (!row) return;
+ if (e.type === 'keydown' && e.key !== 'Enter' && e.key !== ' ') return;
+ if (e.type === 'keydown') e.preventDefault();
+ selectNode(row.dataset.value);
+ };
+ tbody.addEventListener('click', handler);
+ tbody.addEventListener('keydown', handler);
+ }
+
renderRows();
}
@@ -271,7 +285,7 @@
tbody.innerHTML = nodes.map(n => {
const roleColor = ROLE_COLORS[n.role] || '#6b7280';
- return `
+ return `
| ${favStar(n.public_key, 'node-fav')}${n.name || '(unnamed)'} |
${truncate(n.public_key, 16)} |
${n.role} |
@@ -407,7 +421,5 @@
return (...args) => { clearTimeout(t); t = setTimeout(() => fn(...args), ms); };
}
- window._nodeSelect = selectNode;
-
registerPage('nodes', { init, destroy });
})();
diff --git a/public/packets.js b/public/packets.js
index a19f3ee..857aa9f 100644
--- a/public/packets.js
+++ b/public/packets.js
@@ -290,6 +290,24 @@
}, 250));
fNode.addEventListener('blur', () => { setTimeout(() => fNodeDrop.classList.add('hidden'), 200); });
+ // Delegated click/keyboard handler for table rows
+ const pktBody = document.getElementById('pktBody');
+ if (pktBody) {
+ const handler = (e) => {
+ const row = e.target.closest('tr[data-action]');
+ if (!row) return;
+ if (e.type === 'keydown' && e.key !== 'Enter' && e.key !== ' ') return;
+ if (e.type === 'keydown') e.preventDefault();
+ const action = row.dataset.action;
+ const value = row.dataset.value;
+ if (action === 'select') selectPacket(Number(value));
+ else if (action === 'select-hash') pktSelectHash(value);
+ else if (action === 'toggle-select') { pktToggleGroup(value); pktSelectHash(value); }
+ };
+ pktBody.addEventListener('click', handler);
+ pktBody.addEventListener('keydown', handler);
+ }
+
renderTableRows();
makeColumnsResizable('#pktTable', 'meshcore-pkt-col-widths');
}
@@ -310,10 +328,7 @@
const groupTypeClass = payloadTypeColor(p.payload_type);
const groupSize = p.raw_hex ? Math.floor(p.raw_hex.length / 2) : 0;
const isSingle = p.count <= 1;
- const rowClick = isSingle
- ? `window._pktSelectHash('${p.hash}')`
- : `window._pktToggleGroup('${p.hash}'); window._pktSelectHash('${p.hash}')`;
- html += `
+ html += `
| ${isSingle ? '' : (isExpanded ? '▼' : '▶')} |
${groupRegion ? `${groupRegion}` : '—'} |
${timeAgo(p.latest)} |
@@ -335,7 +350,7 @@
let childPath = [];
try { childPath = JSON.parse(c.path_json || '[]'); } catch {}
const childPathStr = renderPath(childPath);
- html += `
+ html += `
| ${childRegion ? `${childRegion}` : '—'} |
${timeAgo(c.timestamp)} |
${truncate(c.hash || '', 8)} |
@@ -365,7 +380,7 @@
const pathStr = renderPath(pathHops);
const detail = getDetailPreview(decoded);
- return `
+ return `
| ${region ? `${region}` : '—'} |
${timeAgo(p.timestamp)} |
${truncate(p.hash || String(p.id), 8)} |
@@ -741,8 +756,7 @@
})();
// Global handlers
- window._pktSelect = selectPacket;
- window._pktToggleGroup = async (hash) => {
+ async function pktToggleGroup(hash) {
if (expandedHashes.has(hash)) {
expandedHashes.delete(hash);
renderTableRows();
@@ -763,14 +777,14 @@
expandedHashes.add(hash);
renderTableRows();
} catch {}
- };
- window._pktSelectHash = async (hash) => {
+ }
+ async function pktSelectHash(hash) {
// When grouped, find first packet with this hash
try {
const data = await api(`/packets?hash=${hash}&limit=1`);
if (data.packets?.[0]) selectPacket(data.packets[0].id);
} catch {}
- };
+ }
window._pktRefresh = loadPackets;
window._pktBYOP = showBYOP;