style(live): revert auto-format whitespace churn from #1490 (#1514 S1)

PR #1490 reintroduced cosmetic whitespace flips that commit baeb49dc was
supposed to clean up:

- function() <-> function () spacing
- slice(0,3) <-> slice(0, 3) and similar binary-operator spacing
- '0':0x7E <-> '0': 0x7E object-literal spacing
- {} <-> { } empty block

Net behavioral change: zero. 85 lines whitespace-only.

Refs #1514
This commit is contained in:
OpenClaw Bot
2026-05-31 22:44:04 +00:00
parent f142f0ebde
commit 976ccf6db5
+85 -85
View File
@@ -54,7 +54,7 @@
function packetMatchesRegion(packets, obsMap, selected) { function packetMatchesRegion(packets, obsMap, selected) {
if (!selected || !selected.length) return true; if (!selected || !selected.length) return true;
if (!packets || !packets.length) return false; if (!packets || !packets.length) return false;
const sel = selected.map(function (s) { return String(s).toUpperCase(); }); const sel = selected.map(function(s) { return String(s).toUpperCase(); });
for (var i = 0; i < packets.length; i++) { for (var i = 0; i < packets.length; i++) {
var oid = packets[i] && packets[i].observer_id; var oid = packets[i] && packets[i].observer_id;
if (oid == null) continue; if (oid == null) continue;
@@ -195,7 +195,7 @@
// Wire up click handlers on corner buttons // Wire up click handlers on corner buttons
var btns = document.querySelectorAll('.panel-corner-btn[data-panel]'); var btns = document.querySelectorAll('.panel-corner-btn[data-panel]');
for (var i = 0; i < btns.length; i++) { for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', function (e) { btns[i].addEventListener('click', function(e) {
e.stopPropagation(); e.stopPropagation();
var panelId = this.getAttribute('data-panel'); var panelId = this.getAttribute('data-panel');
onCornerClick(panelId); onCornerClick(panelId);
@@ -397,7 +397,7 @@
const pkts = data.packets || []; const pkts = data.packets || [];
return expandToBufferEntriesAsync(pkts); return expandToBufferEntriesAsync(pkts);
}) })
.then(function (replayEntries) { .then(function(replayEntries) {
if (gen !== VCR.replayGen) return; // stale async result — user changed mode if (gen !== VCR.replayGen) return; // stale async result — user changed mode
if (replayEntries.length === 0) { if (replayEntries.length === 0) {
vcrSetMode('PAUSED'); vcrSetMode('PAUSED');
@@ -461,7 +461,7 @@
const filtered = pkts.filter(p => !existingIds.has(p.id)); const filtered = pkts.filter(p => !existingIds.has(p.id));
return expandToBufferEntriesAsync(filtered); return expandToBufferEntriesAsync(filtered);
}) })
.then(function (newEntries) { .then(function(newEntries) {
if (gen !== VCR.replayGen) return; // stale async result if (gen !== VCR.replayGen) return; // stale async result
VCR.buffer = [].concat(newEntries, VCR.buffer); VCR.buffer = [].concat(newEntries, VCR.buffer);
VCR.playhead = 0; VCR.playhead = 0;
@@ -470,7 +470,7 @@
startReplay(); startReplay();
updateTimeline(); updateTimeline();
}) })
.catch(() => { }); .catch(() => {});
} }
function startReplay() { function startReplay() {
@@ -488,7 +488,7 @@
} }
} }
const replayGroups = [...hashGroups.values()].sort((a, b) => a.ts - b.ts); const replayGroups = [...hashGroups.values()].sort((a, b) => a.ts - b.ts);
console.log('[vcr] ' + replayGroups.length + ' groups from ' + VCR.buffer.length + ' buffer entries. Top 3:', replayGroups.slice(0, 3).map(g => g.packets.length + ' obs')); console.log('[vcr] ' + replayGroups.length + ' groups from ' + VCR.buffer.length + ' buffer entries. Top 3:', replayGroups.slice(0,3).map(g => g.packets.length + ' obs'));
let groupIdx = 0; let groupIdx = 0;
function tick() { function tick() {
@@ -531,7 +531,7 @@
.then(data => { .then(data => {
const pkts = data.packets || []; const pkts = data.packets || [];
if (pkts.length === 0) return false; if (pkts.length === 0) return false;
return expandToBufferEntriesAsync(pkts).then(function (newEntries) { return expandToBufferEntriesAsync(pkts).then(function(newEntries) {
if (gen !== VCR.replayGen) return false; // stale if (gen !== VCR.replayGen) return false; // stale
VCR.buffer = VCR.buffer.concat(newEntries); VCR.buffer = VCR.buffer.concat(newEntries);
return true; return true;
@@ -579,7 +579,7 @@
clickablePaths.push(entry); clickablePaths.push(entry);
pruneClickablePaths(Date.now()); pruneClickablePaths(Date.now());
let dismissTimer = null; let dismissTimer = null;
poly.on('click', function (e) { poly.on('click', function(e) {
if (dismissTimer) clearTimeout(dismissTimer); if (dismissTimer) clearTimeout(dismissTimer);
const html = buildClickablePathPopupHtml(typeName, color, hopNames, tsMs, hash); const html = buildClickablePathPopupHtml(typeName, color, hopNames, tsMs, hash);
L.popup({ maxWidth: 280, className: 'path-info-popup' }) L.popup({ maxWidth: 280, className: 'path-info-popup' })
@@ -611,28 +611,28 @@
// 7-segment LCD renderer // 7-segment LCD renderer
const SEG_MAP = { const SEG_MAP = {
'0': 0x7E, '1': 0x30, '2': 0x6D, '3': 0x79, '4': 0x33, '5': 0x5B, '6': 0x5F, '7': 0x70, '0':0x7E,'1':0x30,'2':0x6D,'3':0x79,'4':0x33,'5':0x5B,'6':0x5F,'7':0x70,
'8': 0x7F, '9': 0x7B, '-': 0x01, ':': 0x80, ' ': 0x00, 'P': 0x67, 'A': 0x77, 'U': 0x3E, '8':0x7F,'9':0x7B,'-':0x01,':':0x80,' ':0x00,'P':0x67,'A':0x77,'U':0x3E,
'S': 0x5B, 'E': 0x4F, 'L': 0x0E, 'I': 0x30, 'V': 0x3E, '+': 0x01 'S':0x5B,'E':0x4F,'L':0x0E,'I':0x30,'V':0x3E,'+':0x01
}; };
function drawSegDigit(ctx, x, y, w, h, bits, color) { function drawSegDigit(ctx, x, y, w, h, bits, color) {
const t = Math.max(2, h * 0.12); // segment thickness const t = Math.max(2, h * 0.12); // segment thickness
const g = 1; // gap const g = 1; // gap
const hw = w - 2*g, hh = (h - 3 * g) / 2; const hw = w - 2*g, hh = (h - 3*g) / 2;
ctx.fillStyle = color; ctx.fillStyle = color;
// a=top, b=top-right, c=bot-right, d=bot, e=bot-left, f=top-left, g=mid // a=top, b=top-right, c=bot-right, d=bot, e=bot-left, f=top-left, g=mid
if (bits & 0x40) ctx.fillRect(x + g + t / 2, y, hw - t, t); // a if (bits & 0x40) ctx.fillRect(x+g+t/2, y, hw-t, t); // a
if (bits & 0x20) ctx.fillRect(x + w - t, y + g + t / 2, t, hh - t / 2); // b if (bits & 0x20) ctx.fillRect(x+w-t, y+g+t/2, t, hh-t/2); // b
if (bits & 0x10) ctx.fillRect(x + w - t, y + hh + 2 * g + t / 2, t, hh - t / 2);// c if (bits & 0x10) ctx.fillRect(x+w-t, y+hh+2*g+t/2, t, hh-t/2);// c
if (bits & 0x08) ctx.fillRect(x + g + t / 2, y + h - t, hw - t, t); // d if (bits & 0x08) ctx.fillRect(x+g+t/2, y+h-t, hw-t, t); // d
if (bits & 0x04) ctx.fillRect(x, y + hh + 2 * g + t / 2, t, hh - t / 2); // e if (bits & 0x04) ctx.fillRect(x, y+hh+2*g+t/2, t, hh-t/2); // e
if (bits & 0x02) ctx.fillRect(x, y + g + t / 2, t, hh - t / 2); // f if (bits & 0x02) ctx.fillRect(x, y+g+t/2, t, hh-t/2); // f
if (bits & 0x01) ctx.fillRect(x + g + t / 2, y + hh + g - t / 2, hw - t, t); // g if (bits & 0x01) ctx.fillRect(x+g+t/2, y+hh+g-t/2, hw-t, t); // g
// colon // colon
if (bits & 0x80) { if (bits & 0x80) {
const r = t * 0.6; const r = t * 0.6;
ctx.beginPath(); ctx.arc(x + w / 2, y + h * 0.33, r, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(x+w/2, y+h*0.33, r, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(x + w / 2, y + h * 0.67, r, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(x+w/2, y+h*0.67, r, 0, Math.PI*2); ctx.fill();
} }
} }
function drawLcdText(text, color) { function drawLcdText(text, color) {
@@ -776,7 +776,7 @@
*/ */
var VCR_CHUNK_SIZE = 200; var VCR_CHUNK_SIZE = 200;
function expandToBufferEntriesAsync(pkts) { function expandToBufferEntriesAsync(pkts) {
return new Promise(function (resolve) { return new Promise(function(resolve) {
var entries = []; var entries = [];
var i = 0; var i = 0;
function processChunk() { function processChunk() {
@@ -879,9 +879,9 @@
} else { } else {
const entry = { const entry = {
packets: [pkt], timer: setTimeout(() => { packets: [pkt], timer: setTimeout(() => {
const buffered = propagationBuffer.get(hash); const buffered = propagationBuffer.get(hash);
propagationBuffer.delete(hash); propagationBuffer.delete(hash);
if (buffered) renderPacketTree(buffered.packets); if (buffered) renderPacketTree(buffered.packets);
}, PROPAGATION_BUFFER_MS) }, PROPAGATION_BUFFER_MS)
}; };
propagationBuffer.set(hash, entry); propagationBuffer.set(hash, entry);
@@ -911,7 +911,7 @@
VCR.timelineTimestamps = timestamps.map(t => new Date(t).getTime()); VCR.timelineTimestamps = timestamps.map(t => new Date(t).getTime());
VCR.timelineFetchedScope = scopeMs; VCR.timelineFetchedScope = scopeMs;
} }
} catch (e) { /* ignore */ } } catch(e) { /* ignore */ }
} }
function updateTimeline() { function updateTimeline() {
@@ -1232,7 +1232,7 @@
const w = size.x + padX * 2; const w = size.x + padX * 2;
const h = size.y + padY * 2; const h = size.y + padY * 2;
const dpr = window.devicePixelRatio || 1; const dpr = window.devicePixelRatio || 1;
// Updating width/height automatically clears the canvas // Updating width/height automatically clears the canvas
animCanvas.width = w * dpr; animCanvas.width = w * dpr;
@@ -1279,8 +1279,8 @@
function _liveResolveTile(dark) { function _liveResolveTile(dark) {
if (!dark) return { url: TILE_LIGHT, attribution: '© OpenStreetMap © CartoDB', refUrl: null }; if (!dark) return { url: TILE_LIGHT, attribution: '© OpenStreetMap © CartoDB', refUrl: null };
const reg = window.MC_TILE_PROVIDERS || {}; const reg = window.MC_TILE_PROVIDERS || {};
const id = (typeof window.MC_getDarkTileProvider === 'function') ? window.MC_getDarkTileProvider() : 'carto-dark'; const id = (typeof window.MC_getDarkTileProvider === 'function') ? window.MC_getDarkTileProvider() : 'carto-dark';
const p = reg[id] || reg['carto-dark'] || {}; const p = reg[id] || reg['carto-dark'] || {};
return { return {
url: p.url || p.baseUrl || TILE_DARK, url: p.url || p.baseUrl || TILE_DARK,
attribution: p.attribution || '© OpenStreetMap © CartoDB', attribution: p.attribution || '© OpenStreetMap © CartoDB',
@@ -1293,7 +1293,7 @@
if (tileLayer.options) tileLayer.options.attribution = r.attribution; if (tileLayer.options) tileLayer.options.attribution = r.attribution;
if (dark && r.refUrl) { if (dark && r.refUrl) {
if (!_liveDarkRefLayer) { if (!_liveDarkRefLayer) {
_liveDarkRefLayer = L.tileLayer(r.refUrl, {maxZoom: 19, attribution: r.attribution}).addTo(map); _liveDarkRefLayer = L.tileLayer(r.refUrl, { maxZoom: 19, attribution: r.attribution }).addTo(map);
} else { } else {
_liveDarkRefLayer.setUrl(r.refUrl); _liveDarkRefLayer.setUrl(r.refUrl);
} }
@@ -1308,21 +1308,21 @@
} }
} }
const _liveInitTile = _liveResolveTile(isDark); const _liveInitTile = _liveResolveTile(isDark);
let tileLayer = L.tileLayer(_liveInitTile.url, {maxZoom: 19, attribution: _liveInitTile.attribution}).addTo(map); let tileLayer = L.tileLayer(_liveInitTile.url, { maxZoom: 19, attribution: _liveInitTile.attribution }).addTo(map);
if (isDark && _liveInitTile.refUrl) { if (isDark && _liveInitTile.refUrl) {
_liveDarkRefLayer = L.tileLayer(_liveInitTile.refUrl, {maxZoom: 19, attribution: _liveInitTile.attribution}).addTo(map); _liveDarkRefLayer = L.tileLayer(_liveInitTile.refUrl, { maxZoom: 19, attribution: _liveInitTile.attribution }).addTo(map);
} }
if (typeof window.MC_applyTileFilter === 'function') window.MC_applyTileFilter(); if (typeof window.MC_applyTileFilter === 'function') window.MC_applyTileFilter();
// Swap tiles when theme changes // Swap tiles when theme changes
const _themeObs = new MutationObserver(function() { const _themeObs = new MutationObserver(function () {
const dark = document.documentElement.getAttribute('data-theme') === 'dark' || const dark = document.documentElement.getAttribute('data-theme') === 'dark' ||
(document.documentElement.getAttribute('data-theme') !== 'light' && window.matchMedia('(prefers-color-scheme: dark)').matches); (document.documentElement.getAttribute('data-theme') !== 'light' && window.matchMedia('(prefers-color-scheme: dark)').matches);
_liveSyncDarkTiles(dark); _liveSyncDarkTiles(dark);
}); });
_themeObs.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] }); _themeObs.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });
// #1420 — re-render on customizer change. // #1420 — re-render on customizer change.
window.addEventListener('mc-tile-provider-changed', function() { window.addEventListener('mc-tile-provider-changed', function () {
const dark = document.documentElement.getAttribute('data-theme') === 'dark' || const dark = document.documentElement.getAttribute('data-theme') === 'dark' ||
(document.documentElement.getAttribute('data-theme') !== 'light' && window.matchMedia('(prefers-color-scheme: dark)').matches); (document.documentElement.getAttribute('data-theme') !== 'light' && window.matchMedia('(prefers-color-scheme: dark)').matches);
_liveSyncDarkTiles(dark); _liveSyncDarkTiles(dark);
@@ -1348,7 +1348,7 @@
injectSVGFilters(); injectSVGFilters();
AreaFilter.init(document.getElementById('liveAreaFilter')); AreaFilter.init(document.getElementById('liveAreaFilter'));
AreaFilter.onChange(function() { loadNodes(); }); AreaFilter.onChange(function () { loadNodes(); });
await loadNodes(); await loadNodes();
showHeatMap(); showHeatMap();
connectWS(); connectWS();
@@ -1420,7 +1420,7 @@
// (cmd/server/types.go ObserverListResponse) — NOT a top-level array. // (cmd/server/types.go ObserverListResponse) — NOT a top-level array.
// Bug #1136: previously parsed as array → map empty → region filter // Bug #1136: previously parsed as array → map empty → region filter
// dropped every packet. // dropped every packet.
fetch('/api/observers').then(function (r) { return r.json(); }).then(function (data) { fetch('/api/observers').then(function(r) { return r.json(); }).then(function(data) {
setObserverIataMap(buildObserverIataMap(data)); setObserverIataMap(buildObserverIataMap(data));
}).catch(function() { /* leave map empty; filter will hide all when active */ }); }).catch(function() { /* leave map empty; filter will hide all when active */ });
RegionFilter.init(rfEl, { dropdown: true }); RegionFilter.init(rfEl, { dropdown: true });
@@ -1587,7 +1587,7 @@
} }
}); });
nodeFilterInput.addEventListener('blur', function() { nodeFilterInput.addEventListener('blur', function () {
// Slight delay so click on a suggestion can register first. // Slight delay so click on a suggestion can register first.
setTimeout(hideDropdown, 150); setTimeout(hideDropdown, 150);
}); });
@@ -1607,7 +1607,7 @@
} }
// Geo filter overlay // Geo filter overlay
(async function() { (async function () {
try { try {
const gf = await api('/config/geo-filter', { ttl: 3600 }); const gf = await api('/config/geo-filter', { ttl: 3600 });
if (!gf || !gf.polygon || gf.polygon.length < 3) return; if (!gf || !gf.polygon || gf.polygon.length < 3) return;
@@ -1617,7 +1617,7 @@
color: geoColor, weight: 2, opacity: 0.8, color: geoColor, weight: 2, opacity: 0.8,
fillColor: geoColor, fillOpacity: 0.08 fillColor: geoColor, fillOpacity: 0.08
}); });
const bufferPoly = gf.bufferKm > 0 ? (function() { const bufferPoly = gf.bufferKm > 0 ? (function () {
let cLat = 0, cLon = 0; let cLat = 0, cLon = 0;
gf.polygon.forEach(function (p) { cLat += p[0]; cLon += p[1]; }); gf.polygon.forEach(function (p) { cLat += p[0]; cLon += p[1]; });
cLat /= gf.polygon.length; cLon /= gf.polygon.length; cLat /= gf.polygon.length; cLon /= gf.polygon.length;
@@ -1807,14 +1807,14 @@
var tog = document.getElementById(p.togId); var tog = document.getElementById(p.togId);
if (body) body.removeAttribute('hidden'); if (body) body.removeAttribute('hidden');
if (root) { root.classList.remove('is-collapsed'); root.classList.remove('is-expanded'); } if (root) { root.classList.remove('is-collapsed'); root.classList.remove('is-expanded'); }
if (tog) { tog.setAttribute('aria-expanded', 'true'); } if (tog) { tog.setAttribute('aria-expanded', 'true'); }
} }
} }
} }
pairs.forEach(function (p) { pairs.forEach(function (p) {
var tog = document.getElementById(p.togId); var tog = document.getElementById(p.togId);
if (!tog) return; if (!tog) return;
tog.addEventListener('click', function() { tog.addEventListener('click', function () {
var root = document.getElementById(p.rootId); var root = document.getElementById(p.rootId);
var nowExpanded = !(root && root.classList.contains('is-expanded')); var nowExpanded = !(root && root.classList.contains('is-expanded'));
setExpanded(p, nowExpanded); setExpanded(p, nowExpanded);
@@ -1890,9 +1890,9 @@
// Resize clamping (debounced) // Resize clamping (debounced)
var resizeTimer = null; var resizeTimer = null;
window.addEventListener('resize', function() { window.addEventListener('resize', function () {
clearTimeout(resizeTimer); clearTimeout(resizeTimer);
resizeTimer = setTimeout(function() { dragMgr.handleResize(); }, 200); resizeTimer = setTimeout(function () { dragMgr.handleResize(); }, 200);
}); });
} }
@@ -1906,7 +1906,7 @@
const swatch = window.makeRoleMarkerSVG const swatch = window.makeRoleMarkerSVG
? window.makeRoleMarkerSVG(role, color, 14) ? window.makeRoleMarkerSVG(role, color, 14)
: `<span class="live-dot" style="background:${color}" aria-hidden="true"></span>`; : `<span class="live-dot" style="background:${color}" aria-hidden="true"></span>`;
li.innerHTML = `<span class="live-shape-swatch" aria-hidden="true">${swatch}</span> ${(ROLE_LABELS[role] || role).replace(/s$/,'')}`; li.innerHTML = `<span class="live-shape-swatch" aria-hidden="true">${swatch}</span> ${(ROLE_LABELS[role] || role).replace(/s$/, '')}`;
roleLegendList.appendChild(li); roleLegendList.appendChild(li);
} }
} }
@@ -1948,7 +1948,7 @@
// Save/restore map view // Save/restore map view
const savedView = localStorage.getItem('live-map-view'); const savedView = localStorage.getItem('live-map-view');
if (savedView) { if (savedView) {
try { const v = JSON.parse(savedView); map.setView([v.lat, v.lng], v.zoom); } catch { } try { const v = JSON.parse(savedView); map.setView([v.lat, v.lng], v.zoom); } catch {}
} }
map.on('moveend', () => { map.on('moveend', () => {
const c = map.getCenter(); const c = map.getCenter();
@@ -1997,7 +1997,7 @@
item.setAttribute('role', 'menuitem'); item.setAttribute('role', 'menuitem');
item.setAttribute('data-scope', src.dataset.scope); item.setAttribute('data-scope', src.dataset.scope);
item.textContent = src.textContent; item.textContent = src.textContent;
item.addEventListener('click', function() { item.addEventListener('click', function () {
src.click(); // delegate to original handler — keeps single source of truth src.click(); // delegate to original handler — keeps single source of truth
menu.setAttribute('hidden', ''); menu.setAttribute('hidden', '');
moreBtn.setAttribute('aria-expanded', 'false'); moreBtn.setAttribute('aria-expanded', 'false');
@@ -2010,13 +2010,13 @@
e.stopPropagation(); e.stopPropagation();
var open = !menu.hasAttribute('hidden'); var open = !menu.hasAttribute('hidden');
if (open) { menu.setAttribute('hidden', ''); moreBtn.setAttribute('aria-expanded', 'false'); } if (open) { menu.setAttribute('hidden', ''); moreBtn.setAttribute('aria-expanded', 'false'); }
else { menu.removeAttribute('hidden'); moreBtn.setAttribute('aria-expanded', 'true'); } else { menu.removeAttribute('hidden'); moreBtn.setAttribute('aria-expanded', 'true'); }
}); });
// Click outside closes the menu. // Click outside closes the menu.
document.addEventListener('click', function (e) { document.addEventListener('click', function (e) {
if (menu.hasAttribute('hidden')) return; if (menu.hasAttribute('hidden')) return;
if (e.target === moreBtn || moreBtn.contains(e.target) || if (e.target === moreBtn || moreBtn.contains(e.target) ||
e.target === menu || menu.contains(e.target)) return; e.target === menu || menu.contains(e.target)) return;
menu.setAttribute('hidden', ''); menu.setAttribute('hidden', '');
moreBtn.setAttribute('aria-expanded', 'false'); moreBtn.setAttribute('aria-expanded', 'false');
}); });
@@ -2124,7 +2124,7 @@
// Refresh relative timestamps in feed every 10 seconds (#701) // Refresh relative timestamps in feed every 10 seconds (#701)
_feedTimestampInterval = setInterval(function() { _feedTimestampInterval = setInterval(function() {
document.querySelectorAll('.feed-time[data-ts]').forEach(function (el) { document.querySelectorAll('.feed-time[data-ts]').forEach(function(el) {
el.innerHTML = formatLiveTimestampHtml(Number(el.dataset.ts)); el.innerHTML = formatLiveTimestampHtml(Number(el.dataset.ts));
}); });
}, 10000); }, 10000);
@@ -2155,10 +2155,10 @@
_navCleanup.timeout = setTimeout(() => { topNav.classList.add('nav-autohide'); }, 4000); _navCleanup.timeout = setTimeout(() => { topNav.classList.add('nav-autohide'); }, 4000);
} }
}); });
if (_navCleanup.pinned) { if (_navCleanup.pinned) {
pinBtn.classList.add('pinned'); pinBtn.classList.add('pinned');
pinBtn.setAttribute('aria-pressed', 'true'); pinBtn.setAttribute('aria-pressed', 'true');
topNav.classList.remove('nav-autohide'); topNav.classList.remove('nav-autohide');
} }
topNav.appendChild(pinBtn); topNav.appendChild(pinBtn);
} }
@@ -2216,7 +2216,7 @@
const observers = h.observers || []; const observers = h.observers || [];
const recent = h.recentPackets || []; const recent = h.recentPackets || [];
const roleColor = ROLE_COLORS[n.role] || '#6b7280'; const roleColor = ROLE_COLORS[n.role] || '#6b7280';
const roleLabel = (ROLE_LABELS[n.role] || n.role || 'unknown').replace(/s$/,''); const roleLabel = (ROLE_LABELS[n.role] || n.role || 'unknown').replace(/s$/, '');
const hasLoc = n.lat != null && n.lon != null; const hasLoc = n.lat != null && n.lon != null;
const lastSeen = formatLiveTimestampHtml(n.last_seen); const lastSeen = formatLiveTimestampHtml(n.last_seen);
const thresholds = window.getHealthThresholds ? getHealthThresholds(n.role) : { degradedMs: 3600000, silentMs: 86400000 }; const thresholds = window.getHealthThresholds ? getHealthThresholds(n.role) : { degradedMs: 3600000, silentMs: 86400000 };
@@ -2241,9 +2241,9 @@
<tr><td style="color:var(--text-muted);padding:4px 8px 4px 0;">Last Seen</td><td>${lastSeen}</td></tr> <tr><td style="color:var(--text-muted);padding:4px 8px 4px 0;">Last Seen</td><td>${lastSeen}</td></tr>
<tr><td style="color:var(--text-muted);padding:4px 8px 4px 0;">Adverts</td><td>${n.advert_count || 0}</td></tr> <tr><td style="color:var(--text-muted);padding:4px 8px 4px 0;">Adverts</td><td>${n.advert_count || 0}</td></tr>
${'default_scope' in n ? `<tr><td style="color:var(--text-muted);padding:4px 8px 4px 0;">Scope</td><td>${n.default_scope === null ? '<span style="color:var(--text-muted)">—</span>' ${'default_scope' in n ? `<tr><td style="color:var(--text-muted);padding:4px 8px 4px 0;">Scope</td><td>${n.default_scope === null ? '<span style="color:var(--text-muted)">—</span>'
: n.default_scope === '' ? '<span style="color:var(--text-muted)">unknown scope</span>' : n.default_scope === '' ? '<span style="color:var(--text-muted)">unknown scope</span>'
: `<code style="color:var(--accent)">${escapeHtml(n.default_scope)}</code>` : `<code style="color:var(--accent)">${escapeHtml(n.default_scope)}</code>`
}</td></tr>` : ''} }</td></tr>` : ''}
${hasLoc ? `<tr><td style="color:var(--text-muted);padding:4px 8px 4px 0;">Location</td><td>${n.lat.toFixed(5)}, ${n.lon.toFixed(5)}</td></tr>` : ''} ${hasLoc ? `<tr><td style="color:var(--text-muted);padding:4px 8px 4px 0;">Location</td><td>${n.lat.toFixed(5)}, ${n.lon.toFixed(5)}</td></tr>` : ''}
${stats.avgSnr != null ? `<tr><td style="color:var(--text-muted);padding:4px 8px 4px 0;">Avg SNR</td><td>${stats.avgSnr.toFixed(1)} dB</td></tr>` : ''} ${stats.avgSnr != null ? `<tr><td style="color:var(--text-muted);padding:4px 8px 4px 0;">Avg SNR</td><td>${stats.avgSnr.toFixed(1)} dB</td></tr>` : ''}
${stats.avgHops != null ? `<tr><td style="color:var(--text-muted);padding:4px 8px 4px 0;">Avg Hops</td><td>${stats.avgHops.toFixed(1)}</td></tr>` : ''} ${stats.avgHops != null ? `<tr><td style="color:var(--text-muted);padding:4px 8px 4px 0;">Avg Hops</td><td>${stats.avgHops.toFixed(1)}</td></tr>` : ''}
@@ -2382,8 +2382,8 @@
function getFavoritePubkeys() { function getFavoritePubkeys() {
let favs = []; let favs = [];
try { favs = favs.concat(JSON.parse(localStorage.getItem('meshcore-favorites') || '[]')); } catch { } try { favs = favs.concat(JSON.parse(localStorage.getItem('meshcore-favorites') || '[]')); } catch {}
try { favs = favs.concat(JSON.parse(localStorage.getItem('meshcore-my-nodes') || '[]').map(n => n.pubkey)); } catch { } try { favs = favs.concat(JSON.parse(localStorage.getItem('meshcore-my-nodes') || '[]').map(n => n.pubkey)); } catch {}
return favs.filter(Boolean); return favs.filter(Boolean);
} }
@@ -2509,7 +2509,7 @@
for (const op of group.packets) { for (const op of group.packets) {
let opHops = []; let opHops = [];
if (op.path_json) { if (op.path_json) {
try { opHops = getParsedPath(op); } catch { } try { opHops = getParsedPath(op); } catch {}
} else if (op.decoded?.path?.hops) { } else if (op.decoded?.path?.hops) {
opHops = op.decoded.path.hops; opHops = op.decoded.path.hops;
} }
@@ -2578,8 +2578,8 @@
const svgHtml = (window.makeRoleMarkerSVG const svgHtml = (window.makeRoleMarkerSVG
? window.makeRoleMarkerSVG(n.role, null, sizePx) ? window.makeRoleMarkerSVG(n.role, null, sizePx)
: '<svg width="' + sizePx + '" height="' + sizePx + '" viewBox="0 0 ' + sizePx + ' ' + sizePx + : '<svg width="' + sizePx + '" height="' + sizePx + '" viewBox="0 0 ' + sizePx + ' ' + sizePx +
'"><circle cx="' + (sizePx / 2) + '" cy="' + (sizePx / 2) + '" r="' + (sizePx / 2 - 2) + '"><circle cx="' + (sizePx/2) + '" cy="' + (sizePx/2) + '" r="' + (sizePx/2 - 2) +
'" fill="' + fillExpr + '" stroke="var(--mc-marker-stroke-color)" stroke-width="var(--mc-marker-stroke-width)" stroke-opacity="var(--mc-marker-stroke-opacity)"/></svg>'); '" fill="' + fillExpr + '" stroke="var(--mc-marker-stroke-color)" stroke-width="var(--mc-marker-stroke-width)" stroke-opacity="var(--mc-marker-stroke-opacity)"/></svg>');
const icon = L.divIcon({ const icon = L.divIcon({
html: svgHtml, html: svgHtml,
@@ -2714,8 +2714,8 @@
// WS-only nodes: remove to prevent unbounded memory growth // WS-only nodes: remove to prevent unbounded memory growth
if (marker) { if (marker) {
if (nodesLayer) { if (nodesLayer) {
try { nodesLayer.removeLayer(marker); } catch (e) { } try { nodesLayer.removeLayer(marker); } catch (e) {}
if (marker._highlightRing) try { nodesLayer.removeLayer(marker._highlightRing); } catch (e) { } if (marker._highlightRing) try { nodesLayer.removeLayer(marker._highlightRing); } catch (e) {}
} }
} }
delete nodeMarkers[key]; delete nodeMarkers[key];
@@ -2775,7 +2775,7 @@
window._liveVcrSetMode = vcrSetMode; window._liveVcrSetMode = vcrSetMode;
// #1207 test seams: expose production feed mutators so E2E can exercise // #1207 test seams: expose production feed mutators so E2E can exercise
// the real eviction guard / placeholder re-add path (not a test-local copy). // the real eviction guard / placeholder re-add path (not a test-local copy).
window._liveAddFeedItem = function (icon, typeName, payload, hops, color, pkt) { window._liveAddFeedItem = function(icon, typeName, payload, hops, color, pkt) {
return addFeedItem(icon, typeName, payload, hops, color, pkt); return addFeedItem(icon, typeName, payload, hops, color, pkt);
}; };
window._liveRebuildFeedList = function() { return rebuildFeedList(); }; window._liveRebuildFeedList = function() { return rebuildFeedList(); };
@@ -2845,7 +2845,7 @@
} catch { } } catch { }
}; };
ws.onclose = () => setTimeout(connectWS, WS_RECONNECT_MS); ws.onclose = () => setTimeout(connectWS, WS_RECONNECT_MS);
ws.onerror = () => { }; ws.onerror = () => {};
} }
// === UNIFIED PACKET RENDERER === // === UNIFIED PACKET RENDERER ===
@@ -2870,12 +2870,12 @@
} }
// --- Favorites filter --- // --- Favorites filter ---
if (showOnlyFavorites && !packets.some(function (p) { return packetInvolvesFavorite(p); })) return; if (showOnlyFavorites && !packets.some(function(p) { return packetInvolvesFavorite(p); })) return;
// --- Node filter --- // --- Node filter ---
if (nodeFilterKeys.length) { if (nodeFilterKeys.length) {
nodeFilterTotal++; nodeFilterTotal++;
if (!packets.some(function (p) { return packetInvolvesFilterNode(p, nodeFilterKeys); })) return; if (!packets.some(function(p) { return packetInvolvesFilterNode(p, nodeFilterKeys); })) return;
nodeFilterShown++; nodeFilterShown++;
updateNodeFilterUI(); updateNodeFilterUI();
} }
@@ -2895,7 +2895,7 @@
if (h.payloadTypeName === 'ADVERT' && p.pubKey) { if (h.payloadTypeName === 'ADVERT' && p.pubKey) {
var key = p.pubKey; var key = p.pubKey;
if (!nodeMarkers[key] && p.lat != null && p.lon != null && !(p.lat === 0 && p.lon === 0)) { if (!nodeMarkers[key] && p.lat != null && p.lon != null && !(p.lat === 0 && p.lon === 0)) {
var n = { public_key: key, name: p.name || key.slice(0, 8), role: p.role || 'unknown', lat: p.lat, lon: p.lon, _liveSeen: Date.now() }; var n = { public_key: key, name: p.name || key.slice(0,8), role: p.role || 'unknown', lat: p.lat, lon: p.lon, _liveSeen: Date.now() };
nodeData[key] = n; nodeData[key] = n;
addNodeMarker(n); addNodeMarker(n);
if (window.HopResolver) HopResolver.init(Object.values(nodeData)); if (window.HopResolver) HopResolver.init(Object.values(nodeData));
@@ -2919,7 +2919,7 @@
for (const fp of packets) { for (const fp of packets) {
let fpHops = []; let fpHops = [];
if (fp.path_json) { if (fp.path_json) {
try { fpHops = getParsedPath(fp); } catch { } try { fpHops = getParsedPath(fp); } catch {}
} else if (fp.decoded?.path?.hops) { } else if (fp.decoded?.path?.hops) {
fpHops = fp.decoded.path.hops; fpHops = fp.decoded.path.hops;
} }
@@ -2940,7 +2940,7 @@
// --- Rain drops: one per observation --- // --- Rain drops: one per observation ---
var baseHops = (decoded.path?.hops || []).length || 1; var baseHops = (decoded.path?.hops || []).length || 1;
packets.forEach(function (rp, i) { packets.forEach(function(rp, i) {
if (i === 0) { addRainDrop(rp); return; } if (i === 0) { addRainDrop(rp); return; }
var variedHops = Math.max(1, baseHops + Math.floor(Math.random() * 3) - 1); var variedHops = Math.max(1, baseHops + Math.floor(Math.random() * 3) - 1);
setTimeout(function() { addRainDrop(rp, variedHops); }, i * 150); setTimeout(function() { addRainDrop(rp, variedHops); }, i * 150);
@@ -3034,11 +3034,11 @@
var ghost = L.circleMarker(hp.pos, { var ghost = L.circleMarker(hp.pos, {
radius: 3, fillColor: ghostColor, fillOpacity: 0.2, color: color, weight: 1, opacity: 0.3 radius: 3, fillColor: ghostColor, fillOpacity: 0.2, color: color, weight: 1, opacity: 0.3
}).addTo(pathsLayer); }).addTo(pathsLayer);
setTimeout((function (g) { return function() { if (pathsLayer.hasLayer(g)) pathsLayer.removeLayer(g); }; })(ghost), GHOST_TIMEOUT_MS); setTimeout((function(g) { return function() { if (pathsLayer.hasLayer(g)) pathsLayer.removeLayer(g); }; })(ghost), GHOST_TIMEOUT_MS);
} }
} }
// Remove dashed line after timeout // Remove dashed line after timeout
setTimeout((function (l) { return function() { if (pathsLayer.hasLayer(l)) pathsLayer.removeLayer(l); }; })(line), GHOST_TIMEOUT_MS); setTimeout((function(l) { return function() { if (pathsLayer.hasLayer(l)) pathsLayer.removeLayer(l); }; })(line), GHOST_TIMEOUT_MS);
} }
// Ghost marker for the final unreached hop // Ghost marker for the final unreached hop
var last = hopPositions[hopPositions.length - 1]; var last = hopPositions[hopPositions.length - 1];
@@ -3062,7 +3062,7 @@
if (resolvedPath && resolvedPath.length === hops.length && window.HopResolver && HopResolver.ready()) { if (resolvedPath && resolvedPath.length === hops.length && window.HopResolver && HopResolver.ready()) {
resolvedMap = HopResolver.resolveFromServer(hops, resolvedPath); resolvedMap = HopResolver.resolveFromServer(hops, resolvedPath);
// Fill in any null entries from client-side fallback, preserving sender GPS context // Fill in any null entries from client-side fallback, preserving sender GPS context
var nullHops = hops.filter(function (h, i) { return !resolvedPath[i] && !resolvedMap[h]; }); var nullHops = hops.filter(function(h, i) { return !resolvedPath[i] && !resolvedMap[h]; });
if (nullHops.length) { if (nullHops.length) {
var fallback = HopResolver.resolve(nullHops, senderLat, senderLon, null, null, null); var fallback = HopResolver.resolve(nullHops, senderLat, senderLon, null, null, null);
for (var k in fallback) resolvedMap[k] = fallback[k]; for (var k in fallback) resolvedMap[k] = fallback[k];
@@ -3144,7 +3144,7 @@
} }
if (!animLayer) return; if (!animLayer) return;
// Audio hook: notify per-hop callback // Audio hook: notify per-hop callback
if (onHop) try { onHop(hopIndex, hopPositions.length, hopPositions[hopIndex]); } catch (e) { } if (onHop) try { onHop(hopIndex, hopPositions.length, hopPositions[hopIndex]); } catch (e) {}
const hp = hopPositions[hopIndex]; const hp = hopPositions[hopIndex];
const isGhost = hp.ghost; const isGhost = hp.ghost;
@@ -3252,10 +3252,10 @@
ringHl.setStyle({ color: color, weight: 3, opacity: 0.95, fillOpacity: 0, fill: false }); ringHl.setStyle({ color: color, weight: 3, opacity: 0.95, fillOpacity: 0, fill: false });
ringHl.setRadius(baseSize / 2 + 4); ringHl.setRadius(baseSize / 2 + 4);
setTimeout(() => { setTimeout(() => {
try { ringHl.setStyle({ opacity: 0.4, weight: 2 }); ringHl.setRadius(baseSize / 2 + 8); } catch (e) { } try { ringHl.setStyle({ opacity: 0.4, weight: 2 }); ringHl.setRadius(baseSize / 2 + 8); } catch (e) {}
}, 200); }, 200);
setTimeout(() => { setTimeout(() => {
try { ringHl.setStyle({ opacity: 0, weight: 0 }); } catch (e) { } try { ringHl.setStyle({ opacity: 0, weight: 0 }); } catch (e) {}
}, 700); }, 700);
} catch (e) { /* circleMarker absent — ignore */ } } catch (e) { /* circleMarker absent — ignore */ }
} }
@@ -3482,7 +3482,7 @@
// Remove old chars beyond trail length // Remove old chars beyond trail length
while (charMarkers.length > TRAIL_LEN) { while (charMarkers.length > TRAIL_LEN) {
const old = charMarkers.shift(); const old = charMarkers.shift();
try { animLayer.removeLayer(old.marker); } catch { } try { animLayer.removeLayer(old.marker); } catch {}
} }
// Fade existing chars // Fade existing chars
@@ -3523,8 +3523,8 @@
} }
const ft = Math.min(1, (now - fadeStart) / 300); const ft = Math.min(1, (now - fadeStart) / 300);
if (ft >= 1) { if (ft >= 1) {
for (const cm of charMarkers) try { animLayer.removeLayer(cm.marker); } catch { } for (const cm of charMarkers) try { animLayer.removeLayer(cm.marker); } catch {}
try { pathsLayer.removeLayer(trail); } catch { } try { pathsLayer.removeLayer(trail); } catch {}
charMarkers.length = 0; charMarkers.length = 0;
} else { } else {
const op = 1 - ft; const op = 1 - ft;
@@ -3661,8 +3661,8 @@
const f = activeFades[i]; const f = activeFades[i];
if (!pathsLayer) continue; if (!pathsLayer) continue;
const fadeElapsed = now - f.lastFade; const fadeElapsed = now - f.lastFade;
if (fadeElapsed >= 52) { if (fadeElapsed >= 52) {
const fadeTicks = Math.min(Math.floor(fadeElapsed / 52), 4); const fadeTicks = Math.min(Math.floor(fadeElapsed / 52), 4);
f.lastFade = now; f.lastFade = now;
f.opacity -= 0.1 * fadeTicks; f.opacity -= 0.1 * fadeTicks;
if (f.opacity <= 0) { if (f.opacity <= 0) {
@@ -3696,10 +3696,10 @@
lineCap: 'round', dashArray: anim.isDashed ? '4 6' : null lineCap: 'round', dashArray: anim.isDashed ? '4 6' : null
}).addTo(pathsLayer); }).addTo(pathsLayer);
recentPaths.push({ line, glowLine: contrail, time: Date.now() }); recentPaths.push({ line, glowLine: contrail, time: Date.now() });
while (recentPaths.length > 5) { while (recentPaths.length > 5) {
const old = recentPaths.shift(); const old = recentPaths.shift();
if (pathsLayer) { pathsLayer.removeLayer(old.line); pathsLayer.removeLayer(old.glowLine); } if (pathsLayer) { pathsLayer.removeLayer(old.line); pathsLayer.removeLayer(old.glowLine); }
activeFades = activeFades.filter(f => f.line !== old.line); activeFades = activeFades.filter(f => f.line !== old.line);
} }
@@ -4092,7 +4092,7 @@
}; };
registerPage('live', { registerPage('live', {
init: function (app, routeParam) { init: function(app, routeParam) {
_themeRefreshHandler = () => { _themeRefreshHandler = () => {
rebuildFeedList(); rebuildFeedList();
if (activeNodeDetailKey) showNodeDetail(activeNodeDetailKey); if (activeNodeDetailKey) showNodeDetail(activeNodeDetailKey);