- Each routed echo line in the Messages table gets a map icon that
switches to the Map view with that message + echo pre-selected and
its path drawn; the sidebar scrolls the selection into view
- Map sidebar entries show the message content under the sender: one
truncated line normally, full text when the message is selected;
sidebar entry markup switched from innerHTML to DOM building so
network-supplied sender/content strings are never parsed as HTML
- Fix: path drawing (fitBounds) now runs after invalidateSize in the
same deferred step - drawing immediately after unhiding the map
container computed bounds against a stale size and left the map at
world zoom when jumping from the table
Verified live via Playwright: jump selects the right echo (route
chips match the sidebar entry), path draws at zoom 10, previews
truncate on unselected entries and expand on the selected one.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- HB column in the message table: distinct hash sizes (bytes per hop)
across the message's routed echoes, e.g. '1' or '1,2'; em dash when
no routed echo
- HB filter (Any/1-byte/2-byte/3-byte): matches when any routed echo
uses that hash size; 0-hop echoes are excluded since their stored
hash_size is meaningless; combines with the other filters and is
reset by Clear
Verified live via Playwright: 1B/2B/3B filters each return only
messages with a matching routed echo (183/166/9 of 606, zero false
positives), combined hops+HB filtering works, Clear resets.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Third view toggle: Map. Leaflet lazy-init on first activation with
invalidateSize() once the container is visible; OSM tiles as in the
main app map
- Base layer: repeater contacts with geo plotted as purple circle
markers (name + pubkey prefix popup), from /api/contacts/cached
- Sidebar lists filtered messages that have routed echoes; click a
message to see its echoes, click an echo to draw it
- Hop resolution by pubkey prefix: single geo candidate draws a solid
hop point; collisions render all candidates as amber markers plus a
legend row with clickable pick-chips (pick resolves the hop and
redraws); unknown hops are listed as 'unknown / no position'
- Polyline follows path order with the sender (name-matched contact) as
origin when available; segments bridging unresolved hops are dashed;
map fits bounds to the drawn path
- Clear button erases the drawn path; stale selections are dropped when
filters change underneath
Verified live via Playwright: 252 repeater markers, path + legend
render, ambiguous-hop candidate pick adds a polyline segment, clear
empties the layer, view toggling stays stable with no page errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Messages / Repeaters view toggle in the toolbar; stats aggregate over
the currently filtered messages so both views share one filter state
- Per exact hash token: Relayed (echoes containing the token anywhere),
distinct Messages, As-last-hop count, and Avg SNR counted only over
echoes where the token is the final hop (SNR is measured at our
receiver, so it is never attributed to intermediate hops)
- Sortable numeric columns (default Relayed desc, nulls always last)
- Token -> contact enrichment via /api/contacts/cached?format=full
pubkey prefix match: single candidate shows the name, collisions show
'ambiguous (n)' with candidate names in the tooltip (matcher shared
with the stage-5 map)
- Clicking a stats row applies that token as the message filter and
switches back to the Messages view
Verified live via Playwright: aggregates match an in-page manual
recount exactly, sort orders correct, row-click cross-navigation works,
1-byte and 2-byte tokens kept distinct.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Message rows expand to show each echo as per-hop repeater hash chips
(A3 -> 3B -> 5E) with direction badge, SNR and received time; tokens
split per that echo's own hash_size (same logic as showPathsPopup),
0-hop echoes shown as 'Direct (flood, 0 hops)'
- Chip click copies the repeater hash, echo line click copies the comma
route (parity with the chat path popup)
- Filter bar (client-side, combinable, 150ms debounce): hop count
(any/0/1/2/3/4+, matches if any echo has that hop count), repeater
hash token (hex-normalized prefix match, bridges mixed hash sizes),
sender substring; Clear button appears when any filter is active
- 'N of M messages' counter and a dedicated empty state when filters
match nothing
Verified live via Playwright: expand/collapse, both copy actions,
hops=2 yields only rows with a 2-hop echo (39/602), combined
token+hops filter, sender filter, clear resets to full set.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Full-screen iframe panel wired per the add-fullscreen-panel checklist:
- /path-analyzer Flask view + standalone template (theme bootstrap before
CSS, local vendor assets, Leaflet included for the stage-5 map view)
- path-analyzer.js: days selector (1/3/5/7), message table (time, channel,
sender, preview, copyable packet hash, hops, echo count), spinner/empty
states, local toast helpers fed from /api/ui/settings
- #pathAnalyzerModal in index.html, iframe src set on show.bs.modal for
fresh data each open; FAB button + purple gradient class
- menu entry in #mainMenu (Tools) + placement radio row in Settings ->
Appearance; registered in ITEM_PLACEMENT_DEFS/DEFAULTS (default: menu)
- style.css: #pathAnalyzerModal added to all three narrow-viewport
fullscreen-override selector lists
Verified live via Playwright at 1280px and 390px, light + dark: modal
fills the viewport exactly, placement radios move the entry both ways,
iframe reloads on every open.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>