diff --git a/public/index.html b/public/index.html index 80f9a361..d751162c 100644 --- a/public/index.html +++ b/public/index.html @@ -86,7 +86,7 @@ - + diff --git a/public/packets.js b/public/packets.js index add58676..82464b8e 100644 --- a/public/packets.js +++ b/public/packets.js @@ -129,13 +129,16 @@ } } - function renderHop(h) { - return HopDisplay.renderHop(h, hopNameCache[h], { hexMode: showHexHashes }); + function renderHop(h, observerId) { + // Use per-packet cache key if observer context available (ambiguous hops differ by region) + const cacheKey = observerId ? h + ':' + observerId : h; + const entry = hopNameCache[cacheKey] || hopNameCache[h]; + return HopDisplay.renderHop(h, entry, { hexMode: showHexHashes }); } - function renderPath(hops) { + function renderPath(hops, observerId) { if (!hops || !hops.length) return '—'; - return hops.map(renderHop).join('→'); + return hops.map(h => renderHop(h, observerId)).join('→'); } let directPacketId = null; @@ -418,6 +421,32 @@ } if (allHops.size) await resolveHops([...allHops]); + // Per-observer batch resolve for ambiguous hops (context-aware disambiguation) + const hopsByObserver = {}; + for (const p of packets) { + if (!p.observer_id) continue; + try { + const path = JSON.parse(p.path_json || '[]'); + const ambiguous = path.filter(h => hopNameCache[h]?.ambiguous); + if (ambiguous.length) { + if (!hopsByObserver[p.observer_id]) hopsByObserver[p.observer_id] = new Set(); + ambiguous.forEach(h => hopsByObserver[p.observer_id].add(h)); + } + } catch {} + } + // Batch resolve — one API call per observer (typically 4-5 observers) + await Promise.all(Object.entries(hopsByObserver).map(async ([obsId, hopsSet]) => { + try { + const params = new URLSearchParams({ hops: [...hopsSet].join(','), observer: obsId }); + const result = await api(`/resolve-hops?${params}`); + if (result?.resolved) { + for (const [k, v] of Object.entries(result.resolved)) { + hopNameCache[k + ':' + obsId] = v; + } + } + } catch {} + })); + // Restore expanded group children if (groupByHash && expandedHashes.size > 0) { for (const hash of expandedHashes) { @@ -927,7 +956,7 @@ const groupRegion = headerObserverId ? (observers.find(o => o.id === headerObserverId)?.iata || '') : ''; let groupPath = []; try { groupPath = JSON.parse(headerPathJson || '[]'); } catch {} - const groupPathStr = renderPath(groupPath); + const groupPathStr = renderPath(groupPath, headerObserverId); const groupTypeName = payloadTypeName(p.payload_type); const groupTypeClass = payloadTypeColor(p.payload_type); const groupSize = p.raw_hex ? Math.floor(p.raw_hex.length / 2) : 0; @@ -959,7 +988,7 @@ const childRegion = c.observer_id ? (observers.find(o => o.id === c.observer_id)?.iata || '') : ''; let childPath = []; try { childPath = JSON.parse(c.path_json || '[]'); } catch {} - const childPathStr = renderPath(childPath); + const childPathStr = renderPath(childPath, child.observer_id); html += `