Used c.last_advert (numeric Unix timestamp) instead of c.last_seen
(ISO 8601 string) which caused formatTimeAgo() to return "Invalid Date".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shows a download icon next to the device path display in Contact Info.
Clicking it imports the current device path into configured paths as
primary, with hash_size properly decoded from out_path_len.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ID mode searches by first N hex chars of public key (2/4/6 chars
depending on selected hash size). Placeholder updates dynamically.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New `contact_paths` table for storing multiple user-configured paths per contact
- New `no_auto_flood` column on contacts to prevent automatic DIRECT→FLOOD reset
- Path rotation during DM retry: cycles through configured paths before optional flood fallback
- REST API for path CRUD, reorder, reset-to-flood, repeater listing
- Path management UI in Contact Info modal: add/delete/reorder paths, repeater picker with uniqueness warnings, hash size selector (1B/2B/3B)
- "No Flood" per-contact toggle in modal footer
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
req_status, req_acl, req_neighbours, req_mma use send_binary_req
which calculates timeout from suggested_timeout/800. After firmware
updates this can be too short, causing instant timeouts. Adding
min_timeout=15 ensures we wait at least 15 seconds for a response.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update all documentation to reflect features added since last doc update:
- README: new features list, gallery screenshots, development status
- User Guide: global search, console commands, device dashboard, settings,
system log, backup, updated DM and contact management sections
- Architecture: complete API reference, WebSocket namespaces, updated
project structure and database tables
- Troubleshooting: remove v1 bridge references, add UI-based backup,
system log references
- Gallery: add 4 new screenshots (search, filtering, settings, system log),
update 12 existing screenshots
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Group Chat tab in Settings with configurable quote byte limit.
When quoting a message longer than the limit, a dialog asks whether
to use full or truncated quote (with editable byte count).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The MeshCore community uses "companion" not "client" for type 1 nodes.
Rename the CLI label to COM across all UI, API, JS, and docs to align
with official terminology. Includes cache migration for old CLI entries.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace vertical form fields with table rows for less screen space.
Descriptions moved to (i) tooltip icons on hover/touch.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace hardcoded DM retry logic with user-configurable settings stored
in app_settings DB. Settings modal opens from menu with tab-based UI
(ready for future settings tabs). Defaults: 3 direct + 1 flood retries
(was 8+2), 30s/60s intervals, 60s grace period.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reorganize menu from 2 sections (Network Commands, Configuration) into 4:
- Messages (top, no header) - daily actions
- Network - advert commands
- Tools - Map, Console
- System - Device Info, System Log, Backup, Settings placeholder
Increase navbar button/select touch targets (min 40px) for mobile usability.
Widen offcanvas menu from 280px to 300px.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously ignored and blocked contacts were hidden from the "All Sources"
view, making them only discoverable via dedicated Ignored/Blocked filters.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Database file is now named {device_name}.db (e.g. MarWoj.db) instead of
the generic mc-webui.db. On first boot, mc-webui.db is automatically
renamed once the device name is detected. On subsequent boots, the
existing device-named DB is found by scanning the config directory.
This enables future multi-device support where each MeshCore device
has its own separate database file.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Changed from target="_blank" link to fullscreen modal with iframe,
matching the pattern used by Console, DM, and Contacts modals.
Iframe loads on open and clears on close to manage WebSocket lifecycle.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
In-memory ring buffer (2000 entries) captures all Python log records.
New /logs page streams entries via WebSocket in real-time with:
- Level filter (DEBUG/INFO/WARNING/ERROR)
- Module filter (auto-populated from seen loggers)
- Text search with highlighting
- Auto-scroll with pause/resume
- Dark theme matching Console style
Menu entry added under Configuration section.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All settings (protected_contacts, cleanup_settings, retention_settings,
manual_add_contacts) moved from .webui_settings.json file to SQLite database.
Startup migration auto-imports existing file and renames it to .json.bak.
Added safeguard in _on_new_contact: if firmware fires NEW_CONTACT for a
contact already on the device, skip pending and log a warning. Also added
diagnostic logging showing previous DB state (source, protected) when
contacts reappear as pending.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace fixed calc() heights with flexbox layout so the contact list
fills all remaining viewport space on any screen size
- Make body/main/container chain flex columns so the list can grow
- Reduce vertical spacing between contact name, public key, and
last advert rows for more compact cards
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Existing Contacts: Ignore and Block buttons are now disabled when
contact is protected, matching the existing Delete button behavior
- updateProtectionUI: toggling protection now also enables/disables
Ignore, Block, and Delete buttons dynamically
- Chat: Ignore and Block buttons are hidden in message bubbles for
protected contacts (loads protected pubkeys on init)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove redundant 'Contact Types:' label
- Move type badges above search input
- Place search, Approve, and Ignore buttons in single responsive row
- Add tooltip (i) on Filters header with usage hint
- Add batch Ignore button to ignore all filtered pending contacts
- Remove duplicate filtered count badge from Approve button
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SNR precedes the hop hash: 12.50 > [5e]12.25 > [d1]-8.25 > [e7]-3.00
(each SNR shows link quality, hash shows the next relay node)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- trace: accepts comma-separated hex path (e.g. "trace 5e,d1,e7"),
waits for TRACE_DATA response with proper timeout from device
- stats: fix field names (uptime_secs, queue_len, battery_mv, etc.),
show all radio/packet stats with detail breakdown
- self_telemetry: format LPP sensor data nicely instead of raw dict
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- req_regions: library returns string, not dict — was crashing
with "'str' object has no attribute 'items'"
- req_owner: format like meshcore-cli ("X is owned by Y")
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- get help / set help: detailed parameter descriptions with
explanations, matching meshcore-cli style
- get path_hash_mode: library returns int not Event, fixed check
- set help: now reachable (was behind len(args)>=3 guard)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Was using self_info (which has no firmware data). Now uses
send_device_query() like meshcore-cli, showing model, version,
build date and repeat mode.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- req_clock: parse timestamp from binary hex data (little-endian)
and display as human-readable datetime, matching meshcore-cli
- req_neighbours: new command that fetches neighbour list from
repeater with formatted output (name resolution from device
contacts and DB cache, time ago, SNR)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
meshcore _sync methods return dict (data) or None (error/timeout),
not Event objects. hasattr(dict, 'payload') is always False, causing
instant "timeout" errors. Changed to check `result is not None`.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Clock command now shows datetime like meshcore-cli: "Current time: 2026-03-19 11:39:07 (1773916747)"
- Repeater req_* commands: pass timeout=0 to meshcore library so it uses
device's suggested_timeout instead of hardcoded 30s (matching meshcore-cli behavior)
- Execute timeout raised to 120s to accommodate slow repeater responses
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add device management: get/set params, clock/clock sync, time,
reboot, ver, scope, self_telemetry, node_discover.
Add channel management: get_channel, set_channel, add_channel,
remove_channel. Update help text with all command categories.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 9 new console commands for repeater management:
login, logout, cmd, req_status, req_regions, req_owner,
req_acl, req_clock, req_mma. Add resolve_contact helper
and _parse_time_arg utility. Update help text with categories.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. Delete button now sends public_key instead of name to avoid matching
wrong contacts when multiple share similar names.
2. _on_advertisement adds cache-only contacts to mc.pending_contacts when
manual approval is enabled, so they appear in the pending list after
advertising (even if meshcore fires ADVERTISEMENT instead of NEW_CONTACT).
3. Added Delete button for cache-only contacts with dedicated
/api/contacts/cached/delete endpoint and hard_delete_contact DB method.
4. approve_contact/reject_contact now handle DB-only pending contacts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shows an X button when a conversation is selected, allowing quick
clearing of the search field to find another contact.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add resolveConversationName() that prioritizes device contacts over
backend display_name (which falls back to pubkey when DB JOIN fails)
- Add isPubkey() guard to prevent overwriting good names with hex strings
- Add arrow key navigation (Up/Down) in searchable contact dropdown
- Auto-focus message input after selecting contact from dropdown
- Skip filtering when search input contains a pubkey (show all contacts)
- Keep search input and placeholder in sync with best known name
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Prevent mousedown on dropdown from stealing focus (which closed the
dropdown before click could register on desktop)
- After selecting from dropdown, override search input with the known
name to guarantee correct display even if prefix match fails
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When restoring a conversation from localStorage, the saved ID may have
a different pubkey prefix length than the API returns (e.g. pk_e4ce0a07
vs pk_e4ce0a075359459f...). Now selectConversation() does prefix
matching against dmConversations and upgrades the stored ID, so the
display name is resolved correctly instead of showing raw pubkey prefix.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Redesign DM chat contact selector:
- Replace <select> dropdown with searchable text input + filtered dropdown
- Show only device contacts (from /api/contacts/detailed), not all cached
- Sort contacts alphabetically, conversations by recency
- Type badge (CLI/REP/ROOM/SENS) shown in dropdown items
- Keyboard support: Enter selects first match, Escape closes
Add Contact Info modal (replaces Retry toggle in header):
- Shows contact name, type, public key, last advert, path/route, GPS
- Auto Retry toggle moved into modal footer
- Designed for future extensibility (manual path setting etc.)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a DM is retried, the retry send generates a NEW ack code. The
backend correctly maps retry ack → dm_id via _pending_acks, but
the WebSocket emit was sending the retry ack code. The frontend DOM
still has data-ack="<original_ack>" from the first send, so it could
never match retry ACKs → delivery checkmark never appeared.
Now both _on_ack() and _confirm_delivery() look up the original
expected_ack from the database before emitting to the frontend.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Path buffer from firmware contains trailing garbage bytes beyond the
actual hop data. out_path_len encodes both hop count (lower 6 bits)
and hash size (upper 2 bits). Now we:
- Truncate out_path to meaningful bytes (hop_count * hash_size)
- Format as readable E7→DE→54→54→D8 instead of raw hex string
- Show hop count derived from actual path arrows
Example: out_path_len=5 with out_path="e7de5454d81c49dfb86f8a"
now correctly displays as "E7→DE→54→54→D8 (5 hops)" instead of
showing the full 11-byte buffer.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Console `contacts` now shows device-only contacts with path info
(matching meshcore-cli format: name, type, pubkey, path)
- New `contacts_all` command shows all contacts (device + cached from DB)
- Contact cards in UI now always show routing mode for device contacts
(Flood, Direct 0 hop, or hex path with hop count)
- Fix path_or_mode computation: prioritize out_path over out_path_len
to handle firmware edge case where out_path exists but out_path_len=-1
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- DIRECT (known path): 10 attempts (8 DIRECT + 2 FLOOD), 30s wait between
- FLOOD (no path): 3 attempts only, 60s wait between
- Prevents flooding the mesh with rapid retries when no path is known
- Longer DIRECT wait gives ACKs more time to return through multi-hop paths
- Log retry mode at task start for easier debugging
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Cancel retry task immediately when _on_ack or PATH handler confirms delivery
- Keep pending_acks for 60s after retry exhaustion so late ACKs are matched
- Prevents orphaned ACKs (no dm_id) when ACK arrives shortly after exhaustion
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of reloading the entire message list when echo data arrives,
now updates only the affected message elements in the DOM:
- Add data-msg-id attribute to message wrappers for targeted lookup
- Add GET /api/messages/<id>/meta endpoint returning metadata for a
single message (computes pkt_payload, looks up echoes, analyzer URL)
- Replace loadMessages() echo handler with refreshMessagesMeta() that
finds messages missing metadata and updates them individually
- Fix path_len=0 treated as falsy (use ?? instead of ||)
Flow: message appears instantly via WebSocket (with SNR + hops), then
~2s later echo data triggers targeted meta fetch → route info and
analyzer button appear smoothly without any chat window reload.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The backend already emits 'echo' SocketIO events when RX_LOG_DATA arrives
with route/path data, but the frontend wasn't listening. Now the frontend
handles echo events with a debounced loadMessages() refresh (2s delay) to
pick up computed pkt_payload, analyzer_url, hops, and route info.
This fixes messages appearing without metadata until manual page refresh.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The SocketIO new_message emit for channel messages was missing snr,
path_len, pkt_payload and analyzer_url fields, causing messages received
via WebSocket to render without metadata until a full page refresh.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Channel names from device already include # prefix — removed hardcoded #
from search results badge. Added (?) help button with search syntax
examples and link to FTS5 docs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>