Commit Graph

14 Commits

Author SHA1 Message Date
MarekWo afb305a61e feat(pathanalyzer): Routes view - hop segment statistics + sequence filter
New 4th view counting consecutive hop segments (n-grams) across all
routed echo paths, regardless of position in the path:
- segment length selector (2/3/4 hops), table sorted by echo count by
  default; columns: Echoes, distinct Messages, As path end (how often
  the segment is the final part of the path reaching us)
- resolved contact names shown under the hash chips (ambiguous/unknown
  marked like elsewhere)
- row click fills the repeater filter with the segment and jumps to the
  message list
- the repeater filter now accepts consecutive sequences chained with
  > or an arrow (e.g. AFE6>6E9A or hash>name); single values behave
  exactly as before, spaces stay usable inside contact names
- view switcher buttons show icons only on xs screens so 4 buttons fit

Idea by Daniel - group paths by recurring hop sequences to see which
routes carry the most traffic.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 21:38:47 +02:00
MarekWo 0d9d9ecd57 feat(pathanalyzer): map view polish - channel label, auto path, 45vh map
- Show the channel name (dimmed, next to the sender) on map-view tiles
- Clicking a message now auto-selects and draws its shortest routed echo
  (fewest hops, ties -> first); re-clicking keeps the user's echo choice
- Mobile: map takes a fixed 45% of the viewport height and the path list
  gets the remaining space (was min 300px map / max 40% list)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 21:08:19 +02:00
MarekWo 944493a56e feat(pathanalyzer): collapsible filter panel on small screens
On viewports below 768px the toolbar collapses to a single line (view
switcher + Filters toggle + counter); filters expand on demand and an
active-filter count badge shows on the toggle when collapsed. Desktop
layout is unchanged (the panel renders with display: contents). Frees
most of the vertical space for the path list on the mobile Map view.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 20:50:11 +02:00
MarekWo 19c35f4a2b feat(pathanalyzer): combined 2/3-byte option in the HB filter
The path-hash-size filter accepts a comma list of sizes; a new
'2/3-byte' option matches messages with any routed echo using a 2- or
3-byte hash.

Verified live: 2/3-byte returns exactly the union of the 2-byte and
3-byte sets (220 = 208 + 12 with zero overlap misses on live data).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 12:14:50 +02:00
MarekWo c1853fe8d7 feat(pathanalyzer): map path styling and manual-pick undo
- Drawn paths now use red (#dc3545) for hop markers and polylines,
  clearly distinct from the purple base repeater dots (origin stays
  green, ambiguous candidates amber)
- Resolved hops render as numbered badges matching the legend order,
  with a permanent name label next to each point (origin included)
- Manual candidate assignments are reversible: a picked hop shows an
  undo icon in the legend, and a 'Reset picks' button clears every
  manual assignment on the current path

Verified live via Playwright: line/marker colors, badge numbers and
name labels, pick -> per-hop undo -> re-pick -> reset-all flow, no
page errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 11:46:32 +02:00
MarekWo 0ad26a66f8 fix(pathanalyzer): Messages and Repeaters views fit narrow screens
Rather than a card-per-row transform (too much scrolling for 400+
messages), narrow viewports (<=576px) keep the table but shed
secondary width:
- Hash, HB and Echoes columns hidden; packet hash + HB surface as a
  line at the top of the expanded detail row instead (still copyable)
- time renders as short date/time stacked on two lines (full timestamp
  on wide screens)
- sender/channel/message-preview columns capped with ellipsis, tighter
  cell padding, headers allowed to wrap
- stats view: Messages and As-last-hop columns hidden on phones
  (Repeater, Contact, Relayed, Avg SNR remain), contact names truncated
- expanded echo paths now wrap fully within the viewport, keeping every
  hop chip and the jump-to-map button reachable

Root cause of the first attempt failing: the media block sat mid-
stylesheet, so later equal-specificity base rules overrode it - it now
sits last, with a comment pinning it there.

Verified live via Playwright at 390px: zero horizontal overflow in
Messages (incl. an expanded 20-hop path, map button on-screen) and
Repeaters; desktop 1280px layout unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 21:53:57 +02:00
MarekWo 0ba0503547 feat(pathanalyzer): repeater filter matches by name as well as hash
The repeater filter input now accepts either form: pure-hex input still
prefix-matches hop hash tokens, and any input also matches the names of
contacts each token resolves to (pubkey-prefix candidates, memoized per
token). With 1-byte hashes a name can match via a colliding candidate,
so the filter is intentionally inclusive: it keeps messages where the
named repeater could be on the path.

Verified live via Playwright: hex prefix regression OK, name search
returns only messages with a token resolving to the named contact,
memoized pass over 606 messages takes <1 ms.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 19:44:32 +02:00
MarekWo 3522dbabef feat(pathanalyzer): message text filter
Free-text case-insensitive substring filter on message content, next to
the other toolbar filters; combines with them and is reset by Clear.

Verified live via Playwright: content match returns only matching rows
(5/606), combines with the sender filter, Clear restores the full set.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 19:42:42 +02:00
MarekWo 9574a7ebee feat(pathanalyzer): jump-to-map from echo rows, message preview in map sidebar
- 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>
2026-07-19 19:36:11 +02:00
MarekWo 67b405251b feat(pathanalyzer): path hash size (HB) column and filter
- 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>
2026-07-19 19:19:00 +02:00
MarekWo dca3d58232 feat(pathanalyzer): map view with path drawing (stage 5)
- 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>
2026-07-19 19:15:59 +02:00
MarekWo ca24d62ca6 feat(pathanalyzer): per-repeater statistics (stage 4)
- 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>
2026-07-19 19:12:34 +02:00
MarekWo 07d2c347ba feat(pathanalyzer): path display and filters (stage 3)
- 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>
2026-07-19 19:01:23 +02:00
MarekWo 9668651a61 feat(pathanalyzer): Path Analyzer panel skeleton (stage 2)
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>
2026-07-19 17:14:20 +02:00