201 Commits

Author SHA1 Message Date
MarekWo
58af37238b fix(ui): move retry counter above Resend button, same line as delivery info
Retry counter now renders as a dm-delivery-meta div above the Resend
button instead of inline next to it, matching the position of the
post-delivery info. Prevents text from crowding the button on short
messages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-28 14:58:31 +01:00
MarekWo
f135c90e61 fix(ui): align DM route popup to the right to prevent overflow
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-28 14:48:27 +01:00
MarekWo
90c1c90ba3 feat(dm): clickable route popup for long delivery paths
Long routes (>4 hops) show truncated with dotted underline; clicking
opens a popup with the full route and hop count, same style as channel
message path popups. Short routes (<=4 hops) display inline as before.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-28 14:44:40 +01:00
MarekWo
3c7f70175f fix(dm): handle FLOOD delivery and old DIRECT path gracefully
Add hex validation to formatDmRoute to avoid garbling old "DIRECT"
values. When no hex route available (FLOOD delivery), fall back to
delivery_route from ACK (e.g. show "FLOOD" stripped of PATH_ prefix).
Ensures delivery meta always shows something useful.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-28 14:28:40 +01:00
MarekWo
885a967348 fix(dm): show delivery route as hex path, add real-time delivery info
Store actual hex path instead of DIRECT/FLOOD labels in delivery_path.
Format route as AB→CD→EF (same as channel messages, truncated if >4
hops). Add dm_delivered_info WebSocket event so delivery meta appears
in real-time without needing page reload. Remove path info from failed
messages since it's not meaningful for undelivered messages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-28 13:21:53 +01:00
MarekWo
677036a831 fix(dm): move retry counter below message, show delivery info visually
Move the attempt counter (e.g. "Attempt 15/24") from next to the status
icon to below the message text, left of the Resend button. Add visible
delivery meta line for delivered/failed messages showing attempt count
and path used. Store attempt info for failed messages too. Replace
Polish abbreviations (ŚK, ŚD, ŚG) with English in all log messages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-28 12:52:00 +01:00
MarekWo
7dbbba57b9 feat(dm): add real-time retry status and persistent delivery info
Show retry progress in DM message bubble via WebSocket:
- "attempt X/Y" counter updates in real-time during retries
- Failed icon (✗) when all retries exhausted
- Delivery info persisted in DB (attempt number, path used)

Backend: emit dm_retry_status/dm_retry_failed socket events,
store delivery_attempt/delivery_path in direct_messages table.
Frontend: socket listeners update status icon and counter,
delivered tooltip shows attempt info and path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-28 12:25:35 +01:00
MarekWo
9be7ae6cc4 fix(ui): always refresh contact data on path_changed event
The path_changed socket handler was skipping the refresh when Contact
Info modal was closed. This meant contactsList stayed stale, so opening
the modal later still showed outdated path info. Now always refreshes
contactsList on any path_changed event.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-28 07:32:33 +01:00
MarekWo
5df9b4b4a2 fix(ui): refresh Contact Info path display in real-time
Path info in Contact Info modal was stale due to 60s server cache
and no refresh after path operations. Now:
- Invalidate contacts cache after reset_path, change_path, path_update
- Emit 'path_changed' socket event on PATH_UPDATE from device
- UI listens and re-renders Contact Info when path changes
- Reset to FLOOD button immediately refreshes the path display

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 20:29:26 +01:00
MarekWo
292d1d91af fix(contacts): restore flexbox list height, remove calc() overrides
The contact list in Existing/Pending Contacts was not using all available
space due to calc(100vh - ...) and max-height rules overriding the
flexbox layout. Remove fixed height constraints from #pendingList and
#existingList in both contacts_base.html and style.css, letting the
flexbox chain (body > main > container > pageContent > list) fill the
remaining viewport space.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 07:54:47 +01:00
MarekWo
54be1796f8 fix(ui): reduce DM sidebar contact name font size to match channel sidebar
Global .contact-name (1.1rem/600) was bleeding into DM sidebar items.
Added explicit 0.88rem/400 override for .dm-sidebar-item .contact-name.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 17:51:26 +01:00
MarekWo
71e00caa55 feat(ui): add dark/light theme switching with Settings toggle
- Create theme.css with CSS custom properties for light/dark themes
- Dark theme inspired by demo landing page (deep navy palette)
- Update style.css: replace ~145 hardcoded colors with CSS variables
- Extract inline styles from index.html, contacts.html, dm.html to style.css
- Add Appearance tab in Settings modal with theme selector
- Bootstrap 5.3 data-bs-theme integration for native dark mode
- Theme persisted in localStorage, applied before CSS loads (no FOUC)
- Console and System Log panels unchanged (already dark themed)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 08:23:26 +01:00
MarekWo
2e6f0d01d6 feat(device): add Share tab with QR code and URI for sharing own contact
Adds a Share tab to the Device Info modal that generates a QR code
and copyable URI (meshcore://contact/add?...) for sharing the device
contact with other users.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 09:12:30 +01:00
MarekWo
ce88ec291f fix(dm): preserve sidebar search filter when conversations refresh
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 07:59:28 +01:00
MarekWo
1e768e799b feat(ui): add channel/contact sidebar for wide screens (desktop/tablet)
On screens >= 992px (lg breakpoint), show a persistent sidebar panel:
- Group chat: channel list with unread badges, active highlight, muted state
- DM: conversation/contact list with search, unread dots, type badges
- Desktop contact header with info button replaces mobile selector
- Mobile/narrow screens unchanged (dropdown/top selector still used)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 07:56:32 +01:00
MarekWo
7b2f721d1d fix(contacts): wrap long public keys in add contact previews
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 21:43:28 +01:00
MarekWo
878d489661 feat(contacts): add contact UI with URI paste, QR scan, and manual entry
Stage 2 of manual contact add feature:
- POST /api/contacts/manual-add endpoint (URI or raw params)
- New /contacts/add page with 3 input tabs (URI, QR code, Manual)
- QR scanning via html5-qrcode (camera + image upload fallback)
- Client-side URI parsing with preview before submission
- Nav card in Contact Management above Pending Contacts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 20:54:41 +01:00
MarekWo
9ee63188d2 feat(contacts): add push-to-device and move-to-cache operations
Enable moving contacts between device and cache directly from the
Existing Contacts UI:
- "To device" button on cache-only contacts (pushes to device)
- "To cache" button on device contacts (removes from device, keeps in DB)

This helps manage the 350-contact device limit by offloading inactive
contacts to cache and restoring them when needed.

- Add DeviceManager.push_to_device() and move_to_cache() methods
- Add API endpoints: POST /contacts/<pk>/push-to-device, move-to-cache
- Add UI buttons with confirm dialogs in contacts.js

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 18:06:26 +01:00
MarekWo
dfc3b1403a fix: prevent page hang when device channel queries block
/api/messages and /api/messages/updates called get_channels_cached()
which blocks on device communication when cache is cold (up to 240s).
Now uses DB-cached channels for pkt_payload computation instead.

Frontend loadMessages() now has a 15s timeout with auto-retry
and clears the loading spinner on error instead of leaving it
spinning indefinitely.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 21:26:26 +01:00
MarekWo
343b6f40a8 fix(ui): rename 'No Flood' toggle to 'Keep path'
'No Flood' was confusing next to the 'Reset to FLOOD' button.
'Keep path' better describes the behavior: don't auto-reset
the path to FLOOD after failed direct retries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 21:12:31 +01:00
MarekWo
08b972b891 fix(paths): separate Reset to FLOOD from Clear Paths
Reset to FLOOD now only resets the device path without deleting
configured paths from the database. New Clear Paths button deletes
all configured paths from DB without touching the device. This lets
users reset to FLOOD to discover new paths while keeping their
configured alternatives intact.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 10:10:40 +01:00
MarekWo
08ba91b9ba fix(paths): allow non-adjacent duplicate hops for 1-byte paths
For 1B hash size, duplicate repeater IDs are valid as long as they
don't appear consecutively (e.g. AA->BB->CC->AA->EE works fine).
For 2B/3B, duplicates remain fully blocked. Applied to all three
input methods: list picker, map picker, and manual entry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 09:38:29 +01:00
MarekWo
ba26b3dc3a fix(paths): prevent duplicate repeater IDs in path
Blocks adding the same hop prefix twice via all three methods:
- List picker: shows warning notification, ignores click
- Map picker: shows warning notification, keeps selection
- Manual entry: validates on Add Path, rejects with error

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 09:19:55 +01:00
MarekWo
796fb917e4 refactor(paths): move Add Path form into its own modal
The path creation form is now a separate modal (z-index 1070) that
opens above Contact Info with its own backdrop, making the UI layers
clearly distinguishable. Map picker modal bumped to z-index 1080 so
it stacks correctly above both Contact Info and Add Path modals.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 08:48:38 +01:00
MarekWo
0bca19e936 fix(paths): add backdrop between map picker and Contact Info modal
Raises the z-index of the map modal (1070) and its backdrop (1060)
so the Contact Info modal behind is visually grayed out, making it
clear which modal is active.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 08:38:25 +01:00
MarekWo
a0a957289e feat(paths): keep map picker open after adding repeater
Map modal no longer closes on Add - resets selection instead so user
can pick multiple repeaters in sequence. Cancel button renamed to Close.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 08:23:21 +01:00
MarekWo
bf00e7c7d3 feat(paths): add repeater map picker for path configuration
Adds a map button (geo icon) next to the list picker in the path form.
Clicking it opens a modal with a Leaflet map showing repeater locations.
User clicks a repeater marker, then clicks Add to append its ID prefix
to the path hex. Includes Cached toggle to show all DB repeaters vs
only device-known ones. Respects current hash size setting (1B/2B/3B).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 08:13:20 +01:00
MarekWo
8aff9be570 fix(map): show correct "Last seen" for cached contacts
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>
2026-03-23 07:50:46 +01:00
MarekWo
aa1a1b203c feat(paths): add import button for current device path
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>
2026-03-22 21:51:53 +01:00
MarekWo
b6dc03dce5 feat(paths): add Name/ID search mode toggle in repeater picker
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>
2026-03-22 21:38:41 +01:00
MarekWo
8cc67f77d5 feat(dm): add multi-path management and per-contact no-flood toggle
- 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>
2026-03-22 21:20:51 +01:00
MarekWo
ce8227247f feat(chat): add quote dialog with configurable quote length
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>
2026-03-21 15:25:38 +01:00
MarekWo
33a71bed17 refactor(ui): rename contact type label CLI to COM (companion)
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>
2026-03-21 14:37:30 +01:00
MarekWo
9e90e30d9f refactor(settings): compact table layout with tooltip info icons
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>
2026-03-21 13:33:19 +01:00
MarekWo
6f1a5462e9 feat(settings): add Settings modal with configurable DM retry parameters
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>
2026-03-21 13:18:03 +01:00
MarekWo
0108ea9149 refactor(menu): reorganize menu into logical sections + larger touch targets
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>
2026-03-21 07:55:59 +01:00
MarekWo
c48666843a fix(contacts): show ignored/blocked contacts in All Sources filter
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>
2026-03-20 21:22:52 +01:00
MarekWo
0110e65b97 feat: add System Log viewer with real-time streaming
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>
2026-03-20 20:34:29 +01:00
MarekWo
670715f57f fix(contacts): disable ignore/block buttons for protected contacts
- 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>
2026-03-20 09:15:44 +01:00
MarekWo
3337e3fdff feat(contacts): compact pending filters with batch ignore
- 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>
2026-03-20 08:30:32 +01:00
MarekWo
f66e95ffa0 fix: contact delete by pubkey, cache contacts as pending, cache delete button
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>
2026-03-18 08:01:56 +01:00
MarekWo
eb19f3cf76 feat(dm): add clear search button (x) next to contact info
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>
2026-03-17 19:35:31 +01:00
MarekWo
e1d3534624 fix(dm): resolve contact names from device, not backend pubkeys
- 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>
2026-03-17 19:13:03 +01:00
MarekWo
50fdee05ed fix(dm): prevent dropdown close on mouse hover, ensure name after select
- 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>
2026-03-17 08:43:48 +01:00
MarekWo
1ecf2f60f0 fix(dm): resolve conversation name via prefix match for saved IDs
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>
2026-03-17 08:35:40 +01:00
MarekWo
8ce5fa85ba feat(dm): searchable contact selector, contact info modal, device-only contacts
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>
2026-03-17 08:27:01 +01:00
MarekWo
3622619ba4 fix(contacts): decode path correctly using MeshCore V1 encoding
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>
2026-03-16 21:26:06 +01:00
MarekWo
5f72f40742 feat(contacts): show path/route info in UI and split console commands
- 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>
2026-03-16 21:01:15 +01:00
MarekWo
3a26da18fd fix(websocket): update message metadata in-place without full chat reload
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>
2026-03-15 12:49:56 +01:00
MarekWo
0e15df430f fix(websocket): listen for echo events to update message metadata in real-time
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>
2026-03-15 12:38:46 +01:00