551 Commits

Author SHA1 Message Date
Kpa-clawbot
71ec5e6fca rename: MeshCore Analyzer → CoreScope (frontend + .squad)
Phase 1 of the CoreScope rename — frontend display strings and
squad agent metadata only.

index.html:
- <title>, og:title, twitter:title → CoreScope
- Brand text span → CoreScope
- og:image/twitter:image URLs → corescope repo (placeholder)
- Cache busters bumped

public/*.js headers (19 files):
- All file header comments updated

public/*.css headers:
- style.css, home.css updated

JavaScript strings:
- app.js: GitHub URL → corescope
- home.js: 3 fallback siteName references
- customize.js: default siteName + heroTitle

Tests:
- test-e2e-playwright.js: title assertion → corescope
- test-frontend-helpers.js: GitHub URL constant
- benchmark.js: header string
- test-all.sh: header string

.squad:
- team.md, casting/history.json
- All 7 agent charters + 5 history files

NOT renamed (intentional):
- localStorage keys (meshcore-*)
- CSS classes (.meshcore-marker)
- Window globals (_meshcore*)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-28 14:03:32 -07:00
Kpa-clawbot
3361643bc0 fix: #208 search results keyboard accessible — tabindex, role, arrow-key nav
- Search result items: tabindex='0', role='option', data-href (replaces inline onclick)
- Delegated click handler via activateSearchItem()
- Keydown handler: Enter/Space activates, ArrowDown/ArrowUp navigates items
- ArrowDown from search input focuses first result
- searchResults container: role='listbox'
- Bump cache busters

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-28 02:43:09 -07:00
Kpa-clawbot
f04f1b8e77 fix: accessibility — chart labels, table scope, form labels (#210, #211, #212)
#210: Add role="img" aria-label to 9 Chart.js canvases in node-analytics.js
and observer-detail.js with descriptive labels.

#211: Add scope="col" to all <th> elements across analytics.js, audio-lab.js,
compare.js, node-analytics.js, nodes.js, observer-detail.js, observers.js,
and packets.js (40+ headers).

#212: Add aria-label to packet filter input and time window select in
packets.js. Add for/id associations to all customize.js inputs: branding,
theme colors, node/type colors, heatmap sliders, onboarding fields, and
export controls.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-28 02:42:01 -07:00
Kpa-clawbot
447c5d7073 fix: mobile responsive — #203 live bottom-sheet, #204 perf layout, #205 nodes col-hide
#203: Live page node detail panel becomes a bottom-sheet on mobile
      (width:100%, bottom:0, max-height:60vh, rounded top corners).
#204: Perf page reduces padding to 12px, perf-cards stack in 2-col
      grid, tables get smaller font/padding on mobile.
#205: Nodes table hides Public Key column on mobile via .col-pubkey
      class (same pattern as packets page .col-region/.col-rpt).

Cache busters bumped in index.html.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-28 02:38:23 -07:00
Kpa-clawbot
2d2e5625ce fix: resolve E2E test failures — engine badge, timing races, hidden pane
- app.js: render engine badge with .engine-badge span (was plain text)
- test: fix #pktRight waitForSelector to use state:'attached' (hidden by detail-collapsed)
- test: fix map heat persist race — wait for async init to restore checkbox state
- test: fix live heat persist race — test via localStorage set+reload instead of click
- test: fix live matrix toggle race — wait for Leaflet tiles before clicking
- test: increase packet detail timeouts for remote server resilience
- test: make close-button test self-contained (navigate if #pktRight missing)
- bump cache busters

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-28 01:10:31 -07:00
Kpa-clawbot
d9523f23a0 fix: harden node detail rendering with Number() casts and Array.isArray guards, fixes #190
Add defensive type safety to node detail page rendering:
- Wrap all .toFixed() calls with Number() to handle string values from Go backend
- Use Array.isArray() for hash_sizes_seen instead of || [] fallback
- Apply same fixes to both full-screen and side-panel views
- Add 9 new tests for renderHashInconsistencyWarning and renderNodeBadges
  with hash_size_inconsistent data (including non-array edge cases)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 21:28:50 -07:00
Kpa-clawbot
47ee63ed55 fix: #191 #192 #193 #194 — repeater-only collision matrix, expand=observations, store-based node health, goRuntime in perf
#191: Hash collision matrix now filters to role=repeater only (routing-relevant)
#192: expand=observations in /api/packets now returns full observation details (txToMap includes observations, stripped by default)
#193: /api/nodes/:pubkey/health uses in-memory PacketStore when available instead of slow SQL queries
#194: goRuntime (heapMB, sysMB, numGoroutine, numGC, gcPauseMs) restored in /api/perf response

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 21:25:19 -07:00
Kpa-clawbot
ef72484ad2 fix: widen trace page layout to fill screen, fixes #175
- Change .traces-page max-width from 1000px to 95vw via CSS variable
  (--trace-max-width) and center with margin: 0 auto
- Increase SVG path graph column spacing from 140px to 200px so nodes
  and labels don't overlap
- Bump cache busters

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 17:23:31 -07:00
Kpa-clawbot
d3347f9d99 fix(analytics): channels table perf + sortable columns (#166, #167)
Performance (#166):
- renderChannelTimeline: replace O(n²) data.find() with O(1) lookup map
- renderChannelTimeline: precompute maxCount once instead of per-point
- renderChannels: pre-build sub-section HTML before single innerHTML write

Sortable columns (#167):
- All 6 channel table columns are now sortable (click header)
- Default sort: last activity descending (latest message first)
- Sort preference persists to localStorage (meshcore-channel-sort)
- Toggles asc/desc on re-click; smart default direction per column type
- Uses existing .sortable/.sort-active CSS patterns on .analytics-table

Tests: 23 new tests for sortChannels, loadChannelSort, saveChannelSort,
channelTheadHtml, channelTbodyHtml (134 total frontend tests, 0 failures)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 15:42:06 -07:00
Kpa-clawbot
744702ccf6 feat(perf): show Go Runtime stats instead of Event Loop on Go backend
When engine=go, the perf page now renders Go-specific runtime stats
(goroutines, GC collections, GC pause times, heap breakdown, CPUs)
instead of the misleading Node.js Event Loop metrics. Falls back to
the existing Node UI when engine is not 'go' or goRuntime data is
missing. Includes color-coded GC pause thresholds.

fixes #153

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 11:49:10 -07:00
Kpa-clawbot
a6f713a09c chore: bump cache busters to trigger CI deploy 2026-03-27 10:40:18 -07:00
Kpa-clawbot
9ca7777851 fix: version-badge link contrast in nav stats bar
Style .version-badge anchor elements to use --nav-text-muted color
instead of browser-default blue. Adds hover state using --nav-text.
Works with both light and dark themes via existing CSS variables.

fixes #139

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 10:14:50 -07:00
Kpa-clawbot
b92e71fa0e refine version badge: clickable links, version only on prod
- Commit hash is now an <a> linking to GitHub commit (full hash in URL, 7-char display)
- Version tag only shown on prod (port 80/443 or no port), linked to GitHub release
- Staging (non-standard port) shows commit + engine only, no version noise
- Detect prod vs staging via location.port
- Updated tests: 16 cases covering prod/staging/links/edge cases
- Bumped cache busters

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 09:54:53 -07:00
Kpa-clawbot
a7a280801a feat: display version and commit hash in stats bar
Add formatVersionBadge() that renders version, short commit hash, and
engine as a single badge in the nav stats area. Format: v2.6.0 · abc1234 [go].
Skips commit when 'unknown' or missing. Truncates commit to 7 chars.
Replaces the standalone engine badge call in updateNavStats().

8 unit tests cover all edge cases (missing fields, v-prefix dedup,
unknown commit, truncation).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 09:52:13 -07:00
Kpa-clawbot
e47b5f85ed feat: display backend engine badge in stats bar
Show [go] or [node] badge in the nav stats bar when /api/stats
returns an engine field. Gracefully hidden when field is absent.

- Add formatEngineBadge() to app.js (top-level, testable)
- Add .engine-badge CSS class using CSS variables
- Add 5 unit tests in test-frontend-helpers.js
- Bump cache busters

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 09:31:32 -07:00
Kpa-clawbot
bad023ccad fix: hide packet detail pane on fresh page load
Add detail-collapsed class to split-layout initial HTML so the empty
right panel is hidden before any packet is selected. The class is
already removed when a packet row is clicked and re-added when the
close button is pressed.

Add 3 tests verifying the detail pane starts collapsed and that
open/close toggling is wired correctly.

Bump cache busters.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 09:10:51 -07:00
Kpa-clawbot
99c23f8b59 feat: add observer packet comparison page (fixes #129)
Add #/compare page that lets users select two observers and compare
which packets each sees. Fetches last 24h of packets per observer,
computes set diff client-side using O(n) Set lookups. Shows summary
cards (both/only-A/only-B), stacked bar, type breakdown, and tabbed
detail tables. URL is shareable via ?a=ID1&b=ID2 query params.

- New file: public/compare.js (comparePacketSets + page module)
- Added compare button to observers page header
- 11 new tests for comparePacketSets (87 total frontend tests)
- Cache busters bumped

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 08:11:30 -07:00
Kpa-clawbot
55db2bef27 fix: auto-update Nodes tab when ADVERT packets arrive via WebSocket
Fixes #131

The Nodes tab required a full page reload to see newly advertised nodes
because loadNodes() cached the node list in _allNodes and never
re-fetched it on WebSocket updates.

Changes:
- WS handler now filters for ADVERT packets only (payload_type 4 or
  payloadTypeName ADVERT), instead of triggering on every packet type
- Uses 5-second debounce to avoid excessive API calls during bursts
- Resets _allNodes cache and invalidates API cache before re-fetching
- loadNodes(refreshOnly) parameter: when true, updates table rows and
  counts without rebuilding the entire panel (preserves scroll position,
  selected node, tabs, filters, and event listeners)
- Extracted isAdvertMessage() as testable helper with window._nodesIsAdvertMessage hook
- 13 new tests (76 total frontend helpers)
- Cache busters bumped

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 08:08:43 -07:00
Kpa-clawbot
65a7f055de fix: dim stale nodes on live map instead of removing them
Fixes #130 — Nodes loaded from the database (API) are now dimmed with
reduced opacity when stale, matching the static map behavior, instead of
being completely removed by pruneStaleNodes(). WS-only (dynamically
added) nodes are still pruned to prevent memory leaks.

Changes:
- loadNodes() marks API-loaded nodes with _fromAPI flag
- pruneStaleNodes() dims _fromAPI nodes (fillOpacity 0.25) vs removing
- Active nodes restore full opacity when refreshed
- 3 new tests for dim/restore/WS-only behavior (63 total passing)
- Cache busters bumped

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 08:02:09 -07:00
Kpa-clawbot
14b7e56403 Show channel hash and decryption status for undecrypted GRP_TXT packets
When a CHANNEL_MSG (GRP_TXT) can't be decrypted, the decoder now includes:
- channelHashHex: zero-padded uppercase hex string of the channel hash byte
- decryptionStatus: 'decrypted', 'no_key', or 'decryption_failed'

Frontend changes:
- Packet list preview shows '🔒 Ch 0xXX (no key)' or '(decryption failed)'
- Detail pane hex breakdown shows channel hash with status label
- Detail pane message area shows channel hash info for undecrypted packets

6 new decoder tests (58 total): channelHashHex formatting, decryptionStatus
for no keys, empty keys, bad keys, and short encrypted data.

Fixes #123

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 07:54:27 -07:00
Kpa-clawbot
c386f119b0 fix: prune stale nodes from Live page counter (fixes #133)
nodeMarkers map in live.js grew unbounded because ADVERT-injected
nodes were never removed. Added time-based pruning using health
thresholds from roles.js (24h for companions/sensors, 72h for
repeaters/rooms). Prune interval runs every 60 seconds.

- Track _liveSeen timestamp on each nodeData entry
- Update timestamp on every ADVERT (new or existing node)
- pruneStaleNodes() removes nodes exceeding silentMs threshold
- 5 new tests verifying pruning logic and threshold behavior
- Cache busters bumped

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 01:47:42 -07:00
Kpa-clawbot
48328e2cb3 fix: fully collapse detail pane on dismiss — table expands to full width
Closes #125

When the ✕ close button (or Escape) is pressed, the detail pane now
fully hides via display:none (CSS class 'detail-collapsed' on the
split-layout container) so the packets table expands to 100% width.
Clicking a packet row removes the class and restores the detail pane.

Previously the pane only cleared its content but kept its 420px width,
leaving a blank placeholder that wasted ~40% of screen space.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 00:04:06 -07:00
Kpa-clawbot
4cfdd85063 fix: resolve 4 issues + optimize E2E test performance
Issues fixed:
- #127: Firefox copy URL - shared copyToClipboard() with execCommand fallback
- #125: Dismiss packet detail pane - close button with keyboard support
- #124: Customize window scrollbar - flex layout fix for overflow
- #122: Last Activity stale times - use last_heard || last_seen

Test improvements:
- E2E perf: replace 19 networkidle waits, cut navigations 14->7, remove 11 sleeps
- 8 new unit tests for copyToClipboard helper (47->55 in test-frontend-helpers)
- 1 new E2E test for packet pane dismiss

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-26 12:41:25 -07:00
you
e340949253 feat: optimize observations table — 478MB → 141MB
Schema v3 migration:
- Replace observer_id TEXT (64-char hex) with observer_idx INTEGER FK
- Drop redundant hash, observer_name, created_at columns
- Store timestamp as epoch integer instead of ISO string
- In-memory dedup Set replaces expensive unique index lookups
- Auto-migration on startup with timestamped backup (never overwrites)
- Detects already-migrated DBs via pragma user_version + column inspection

Fixes:
- disambiguateHops: restore 'known' field dropped during refactor (fba5649)
- Skip MQTT connections when NODE_ENV=test
- e2e test: encodeURIComponent for # channel hashes in URLs
- VACUUM + TRUNCATE checkpoint after migration (not just VACUUM)
- Daily TRUNCATE checkpoint at 2:00 AM UTC to reclaim WAL space

Observability:
- SQLite stats in /api/perf (DB size, WAL size, freelist, row counts, busy pages)
- Rendered in perf dashboard with color-coded thresholds

Tests: 839 pass (89 db + 30 migration + 70 helpers + 200 routes + 34 packet-store + 52 decoder + 255 decoder-spec + 62 filter + 47 e2e)
2026-03-25 22:33:39 +00:00
you
63f2f7c995 refactor: unify live page packet rendering into renderPacketTree()
Major refactor of live.js data flow:

- Replaced animatePacket() and animateRealisticPropagation() with
  single renderPacketTree(packets, isReplay) function
- All paths use the same function: WS arrival, VCR replay, DB load,
  feed card replay button
- VCR fetches use expand=observations to get full observation data
- expandToBufferEntries() extracts per-observer paths from observations
- startReplay() pre-aggregates VCR buffer by hash before playback
- Feed dedup accumulates observation packets for full tree replay
- Longest path shown in feed (scans all observations, not just first)
- Replay button uses full observation set for starburst animation

Server changes:
- WS broadcast includes path_json per observation
- packet-store insert() uses longest path for display (was earliest)

DB changes:
- Removed seed() function and synthetic test data

Not pushed to prod — local testing only.
2026-03-25 06:02:48 +00:00
you
fba5649979 refactor: consolidate hop disambiguation — remove 3 duplicate implementations
- server.js disambiguateHops() now delegates to server-helpers.js
  (was a full copy of the same algorithm, ~70 lines removed)
- live.js resolveHopPositions() now delegates to shared HopResolver
  (was a standalone reimplementation, ~50 lines removed)
- HopResolver.init() called when live page loads/updates node data
- Net -106 lines, same behavior, single source of truth

All unit tests pass (241). E2E 13/16 (3 pre-existing Chromium crashes).
2026-03-24 22:19:16 +00:00
you
459d51f5a5 fix: re-run decollision on zoom regardless of hash labels
zoomend handler was gated on filters.hashLabels — decollision only
re-ran on zoom when hash labels were enabled. Now always re-renders
markers on zoom so pixel offsets stay correct at every zoom level.
2026-03-24 20:57:45 +00:00
you
863ee604be fix: re-run marker decollision on map resize
Added map.on('resize') handler that re-renders markers, recalculating
pixel-based decollision offsets for the new container size. Previously
only zoomend triggered re-render — resize left stale offsets.

Added E2E test verifying markers survive a viewport resize.
2026-03-24 20:55:39 +00:00
you
305da30b88 fix: run map.invalidateSize before marker decollision on every render
On SPA navigation, the map container may not have its final dimensions
when markers render, causing latLngToLayerPoint to return incorrect
pixel coordinates for decollision. This resulted in overlapping markers
that only resolved on a full page refresh.

Fix: call map.invalidateSize() at the start of every renderMarkers()
call, ensuring correct container dimensions before deconfliction runs.
2026-03-24 20:24:26 +00:00
you
d6ea3dd9fd fix: cast snr/rssi to Number before toFixed() — fixes crash on string values
Observer detail, home health timeline, and traces all called
.toFixed() on snr/rssi values that may be strings from the DB.
Wrapping in Number() matches what live.js already does.
2026-03-24 20:17:41 +00:00
you
14ff1821d6 fix: hash-based packet deduplication in Live feed
Root cause: addFeedItem had no dedup logic — each WS message created
a new feed entry regardless of hash. Dedup only worked when the
'Realistic propagation' toggle was ON (which buffers by hash before
calling animateRealisticPropagation). Default mode called animatePacket
directly for every observation, producing duplicate feed entries.

Fix: Added feedHashMap (hash -> {element, count, pkt, addedAt}) that
tracks recent feed items by packet hash. When a packet with a known
hash arrives within 30s, the existing feed item is updated in-place:
- Observation count badge incremented
- Item flashed and moved to top of feed
- No duplicate DOM element created

Also adds data-hash attribute to feed items for testability.

Tests: 5 new Playwright tests in test-live-dedup.js covering:
- Same hash different observers → single entry
- Different hashes → separate entries
- 5 rapid sequential duplicates → single entry with count 5
- Same hash same observer → still deduplicates
- Packets without hash → not deduplicated
2026-03-24 19:35:28 +00:00
you
1bdf41a631 fix: separate heatmap opacity controls for Map and Live pages
- Live page showHeatMap() now reads meshcore-live-heatmap-opacity from
  localStorage and applies it to the canvas element (was hardcoded 0.3)
- Customizer now has two clearly labeled sliders:
  🗺️ Nodes Map — controls the static map page heatmap
  📡 Live Map — controls the live page heatmap
- Each uses its own localStorage key (meshcore-heatmap-opacity vs
  meshcore-live-heatmap-opacity)
- Added E2E tests for live opacity persistence and dual slider existence
- 13/15 E2E tests pass locally (2 fail due to ARM chromium OOM after
  heavy live page tests — CI on x64 will handle them)

Closes #119 properly this time.
2026-03-24 19:25:28 +00:00
you
52d52af6ec fix: force-enable heat toggle when matrix mode is off
Recover from stale localStorage state where heat checkbox stayed
disabled even after matrix/ghosts mode was turned off. Explicitly
sets ht.disabled = false in the else branch of matrix init.

13/13 E2E tests pass locally.
2026-03-24 19:17:17 +00:00
you
16eb7ef07d fix: persist live page heat checkbox + add E2E test
Live page liveHeatToggle now saves to localStorage (meshcore-live-heatmap).
Map page was already fixed but live page was missed.
Added E2E test that verifies persistence across reload.

13/13 E2E tests pass locally.
2026-03-24 17:57:17 +00:00
you
325fdbe50e fix: heatmap opacity flash on new packet arrival
When new data arrived, toggleHeatmap() destroyed and recreated the
heat layer, causing a brief flash at full opacity before the CSS
opacity was applied via setTimeout. Now reuses the existing layer
via setLatLngs() for data updates, and hooks the 'add' event for
immediate opacity on first creation. No more flash.

All 12 E2E tests pass locally.
2026-03-24 17:53:29 +00:00
you
014b30936d fix: heatmap opacity slider affects entire layer, not just blue minimum
The previous implementation used L.heatLayer's minOpacity which only
controlled the opacity of the coolest (blue) gradient stops. Now sets
CSS opacity on the canvas element directly, affecting all gradient
colors uniformly. Closes #119 properly.
2026-03-24 17:26:22 +00:00
you
b63f42ac75 feat: add heatmap opacity slider in customization UI (closes #119) 2026-03-24 16:07:26 +00:00
you
3111d755a2 fix: persist heat checkbox across page reload (closes #120) 2026-03-24 16:07:26 +00:00
you
8efdff420a fix: packet row expand crash — 'child' not defined, should be 'c'
renderPath(childPath, child.observer_id) referenced undefined variable
'child' instead of loop variable 'c'. Crashed the entire render loop
when expanding a grouped packet row.
2026-03-24 14:27:00 +00:00
you
efd7d811ca fix: encrypted payload field sizes match firmware source (Mesh.cpp)
Per firmware: PAYLOAD_VER_1 uses dest(1) + src(1) + MAC(2), not 6+6+4.
Confirmed from Mesh.cpp lines 129-130: uint8_t dest_hash = payload[i++]
and MeshCore.h: CIPHER_MAC_SIZE = 2.

Changed: decodeEncryptedPayload (REQ/RESPONSE/TXT_MSG), decodeAck,
decodeAnonReq (dest 1B + pubkey 32B + MAC 2B), decodePath (1+1+2).
Updated test min-length assertions.
2026-03-24 01:32:58 +00:00
you
5b496a8235 feat: add missing payload types from firmware spec
Added GRP_DATA (0x06), MULTIPART (0x0A), CONTROL (0x0B), RAW_CUSTOM (0x0F)
to decoder.js, app.js display names, and packet-filter.js.
Source: firmware/src/Packet.h PAYLOAD_TYPE definitions.
2026-03-24 01:23:12 +00:00
you
3016493089 fix: use last_heard||last_seen for status in nodes table and map
renderRows() in nodes.js and three places in map.js were using only
n.last_seen to compute active/stale status, ignoring the more recent
n.last_heard from in-memory packets. This caused nodes that were recently
heard but had an old DB last_seen to incorrectly show as stale.

Also adds 29 unit tests for the aging system (getNodeStatus,
getStatusInfo, getStatusTooltip, threshold values).
2026-03-23 23:32:01 +00:00
you
685d48c62c fix: remove duplicate map link from hex breakdown longitude row
Keep the 📍map link in the Location metadata row (goes to app map).
Remove the redundant 📍 Map pill in the hex breakdown (went to Google Maps).
One link, one style.
2026-03-23 23:27:08 +00:00
you
656c8b8a07 fix: remove all /resolve-hops server API calls from packets page
Was making N API calls per observer for ambiguous hops on every page load,
plus another per packet detail view. All hop resolution now uses the
client-side HopResolver which already handles ambiguous prefixes.
Eliminates the main perf regression.
2026-03-23 23:23:08 +00:00
you
4677bff52e fix: remove 200 packet cap from WebSocket live update handler
Was slicing to 200 packets after every live update, truncating the
initial 32K+ packet list. Now keeps all packets.
2026-03-23 23:11:47 +00:00
you
aa181ce8d4 fix: ast.field not node.field in alias resolver, 37 tests passing
ReferenceError: node is not defined — was using wrong variable name.
Verified with 37 tests covering: firmware type names, aliases, route,
numeric ops, string ops, payload dot notation, hops, size, observations,
AND/OR/NOT, parentheses, and error handling.
2026-03-23 23:08:22 +00:00
you
7f948d3d63 fix: packet filter uses firmware type names (GRP_TXT, TXT_MSG, REQ, etc.)
Was using display names like 'Channel Msg' which aren't standard.
Now resolves to firmware names: GRP_TXT, TXT_MSG, REQ, ADVERT, etc.
Also accepts aliases: 'channel', 'dm', 'Channel Msg' all map to the
correct firmware name for convenience.
2026-03-23 23:02:37 +00:00
you
1254aa904a M3: Add tooltips to status labels explaining active/stale thresholds
- Add getStatusTooltip() helper with role-aware explanations
- Tooltips on status labels in: node badges, status explanation, detail table
- Tooltips on map legend active/stale counts per role
- Native title attributes (long-press on mobile)
- Bump cache busters
2026-03-23 22:51:11 +00:00
you
3094b96e07 feat: Packet Filter Language M1 — Wireshark-style filter engine + UI
Add a filter language for the packets page. Users can type expressions like:
  type == Advert && snr > 5
  payload.name contains "Gilroy"
  hops > 2 || route == FLOOD

Architecture: Lexer → Parser → AST → Evaluator(packet) → boolean

- packet-filter.js: standalone IIFE exposing window.PacketFilter
  - Supports: ==, !=, >, <, >=, <=, contains, starts_with, ends_with
  - Logic: &&, ||, !, parentheses
  - Fields: type, route, hash, snr, rssi, hops, observer, size, payload.*
  - Case-insensitive string comparisons, null-safe
  - Self-tests included (node packet-filter.js)
- packets.js: filter input with 300ms debounce, error display, match count
- style.css: filter input states (focus, error, active)
- index.html: script tag added before packets.js
2026-03-23 22:48:59 +00:00
you
418e1a761a Node Aging M2: status filters + localStorage persistence
- Nodes page: Add Active/Stale/All pill button filter
- Nodes page: Expand Last Heard dropdown (Any,1h,2h,6h,12h,24h,48h,3d,7d,14d,30d)
- Map page: Add Active/Stale/All status filter (hides markers, not just fades)
- Map legend: Show active/stale counts per role (e.g. '420 active, 42 stale')
- localStorage persistence for all filters:
  - meshcore-nodes-status-filter
  - meshcore-nodes-last-heard
  - meshcore-map-status-filter
- Bump cache busters
2026-03-23 22:37:52 +00:00