Commit Graph

79 Commits

Author SHA1 Message Date
MarekWo
8a2a693545 fix: Parse node_discover output correctly by removing prompt
The .node_discover command returns prompt line before JSON array.
Added parser to extract only JSON by finding first '[' character
and ignoring everything before it (including 'MarWoj|* .node_discover').

Fixes JSON parsing error: 'Expecting value: line 1 column 1 (char 0)'

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-02 15:02:35 +01:00
MarekWo
75ec789fba feat: Add node discovery feature and improve advert notification
Implemented new "Discover Nodes" feature in Network Commands menu:
- Added .node_discover command to meshcli wrapper (cli.py)
- Created interactive modal with sortable table showing nearby repeaters
- Displays SNR, RSSI, path length, and signal quality indicators
- Added refresh functionality to rescan for nodes

Fixed advert notification to show clean "Advert sent" message
instead of full meshcli output.

Technical changes:
- app/meshcore/cli.py: Added node_discover() function with JSON parsing
- app/routes/api.py: Updated SPECIAL_COMMANDS and execute_special_command()
  to handle node_discover return type and clean advert message
- app/templates/base.html: Added "Discover Nodes" menu button and modal
- app/static/js/app.js: Added discoverNodes() and displayNodeDiscoveryResults()
- README.md: Added documentation for Discover Nodes feature

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-02 14:34:02 +01:00
MarekWo
797096b551 fix: Improve viewport fix to avoid interfering with page initialization
Changed viewport fix approach to be less invasive:
- Removed document.body.style.height modification
- Use document.documentElement.offsetHeight instead
- Trigger resize event instead of modifying DOM
- Increased delay from 100ms to 150ms for better stability

This should prevent potential conflicts with channel loading and
other page initialization code.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-02 06:56:10 +01:00
MarekWo
35d9d89563 fix: Sync DM read status after modal close and add Android PWA viewport fix
Two critical fixes:

1. DM Badge Sync Issue:
   - After closing DM modal, badge showed correct count for 10 seconds
   - Then auto-refresh (running every 10s) used stale dmLastSeenTimestamps
   - Solution: Call loadDmLastSeenTimestampsFromServer() before updating badge
   - This ensures app.js auto-refresh uses current server state

2. Android PWA Viewport Corruption:
   - Viewport corrupted on F5 refresh in Android PWA mode
   - Status bar content hidden under system UI
   - Solution: Force viewport recalculation on page load
   - Scrolls to top and forces reflow after 100ms delay

Testing needed on Android PWA to verify both fixes work correctly.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-02 06:45:10 +01:00
MarekWo
843ec94bf6 fix: Update DM badge using conversations endpoint instead of updates
Changed DM badge update logic to use /api/dm/conversations endpoint
instead of /api/dm/updates?last_seen={} which was causing incorrect
unread counts (showing 99+, then 2, when there should be 0).

The new approach:
- Fetches all conversations with their unread counts
- Sums up unread messages across all conversations
- Updates badge without full page reload (avoiding viewport corruption)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-02 06:24:08 +01:00
MarekWo
abb84b6712 fix: Update DM badge without full page reload to avoid viewport corruption
Replace full page reload with targeted badge update when DM modal closes.
This prevents the viewport corruption issue that was reintroduced by
window.location.reload() in the previous commit.

Problem:
- Previous solution used window.location.reload() to update badges
- This caused viewport corruption (status bar hidden under system UI)
- Same issue we solved by using modals instead of navigation

New solution:
- Fetch latest unread counts from /api/dm/updates when modal closes
- Update only the green DM badge (notification-badge-dm) on notification bell
- No page reload = no viewport corruption
- Keeps modal solution benefits intact

Changes:
- Remove window.location.reload() from hidden.bs.modal event listener
- Add async fetch to /api/dm/updates endpoint
- Update notification-badge-dm element directly using DOM manipulation
- Handle badge creation/update/hiding based on unread count

Result:
- DM badges update correctly after closing modal
- No viewport corruption
- Fast, smooth user experience

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-01 22:09:20 +01:00
MarekWo
46dfe7f7eb fix: Reload page after closing DM modal to update unread badges
Add event listener for DM modal close event that reloads the main page.
This ensures that unread DM badges are properly updated after user reads
messages in the DM modal.

Previously, when using the "Home" button, the page would reload via navigateTo('/').
Now with the "Close" button, the modal just closes without reloading, leaving
stale unread counts in the notification badges.

Changes:
- Add 'hidden.bs.modal' event listener to dmModal
- Call window.location.reload() when DM modal is closed
- This updates all unread badges on the main page

Note: This should not cause viewport corruption issues because:
1. Modal is fully closed before reload
2. No offcanvas or other Bootstrap components are active
3. Regular page reload, not navigation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-01 22:03:52 +01:00
MarekWo
a1f91816af refactor: Improve Contact Management modal pages layout
After removing navbars from modal embedded pages, add proper page headers
and optimize button layout for better mobile UX.

Changes to Pending Contacts page:
- Add H4 page header "Pending Contacts" with badge counter
- Replace two large buttons (Contact Management + Home) with smaller ones
- Change Home button to Refresh button (more useful in modal context)
- Use btn-sm for compact button sizes
- Replace back-buttons class with d-flex gap-2 for simpler layout

Changes to Existing Contacts page:
- Add H4 page header "Existing Contacts" with counter badge
- Same button improvements as Pending Contacts
- Replace Home button with Refresh button

Changes to Contact Management settings:
- Set default "Days of Inactivity" value to 0 (ignore) instead of 2
- This is safer default as 0 means "do not filter by inactivity"

Result:
- Clear page headers indicate current location in modal
- Smaller, more compact buttons save screen space
- Refresh buttons are more useful than Home (which is Close in modal)
- Safer default value for cleanup prevents accidental bulk deletions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-01 21:50:18 +01:00
MarekWo
0a62b9ae04 fix: Remove modal margins to fill entire screen without gaps
Add custom CSS overrides to ensure fullscreen modals fill the entire viewport
without any gaps on top, left, right, or bottom edges.

Changes:
- Force modal-dialog to use 0 margin and 100vw/100vh dimensions
- Remove border and border-radius from modal-content
- Set overflow hidden on modal-body to prevent scrollbars
- Use !important to override Bootstrap default styles

This fixes the issue where modals had visible gaps on the top and left sides
while content was slightly cut off on the right and bottom edges.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-01 19:50:11 +01:00
MarekWo
7986c4f047 refactor: Remove redundant navbars from modals and optimize screen space usage
After testing the modal implementation on Android PWA, removed duplicate
navigation elements and optimized layout to maximize screen real estate.

Changes to DM modal:
- Removed navbar with back button and "DM" title (duplicates modal header)
- Moved conversation selector to top of content as clean dropdown bar
- Changed main container from dynamic height to fixed 100vh

Changes to Contact Management modals:
- Removed navbar with home button from contacts_base.html
- Removed navbar_content blocks from all contact pages (manage, pending, existing)
- Removed padding/margins from base container (px-3 py-4 → p-0)
- Removed row gutters (added g-0 class)
- Removed col width constraint (col-lg-8 mx-auto → col-12)
- Added padding back to individual page content (p-3) for readability

Changes to modal close buttons:
- Replaced small X icon (btn-close) with proper button
- Added "Close" text with X icon for better visibility on mobile
- Used btn-outline-light styling for consistency with green/blue headers

Result:
- Modals now fill entire available screen space without margins
- No duplicate headers or navigation elements
- Clearer, more intuitive close action with labeled button
- Better mobile experience with larger touch targets

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-01 19:42:41 +01:00
MarekWo
f9827ea06c feat: Replace direct navigation with fullscreen modals for DM and Contact Management
Implement modal dialogs to resolve PWA viewport corruption issues on Android.
When navigating between pages using window.location.href, the viewport height
calculation becomes corrupted, causing the status bar to be hidden under system UI.

Changes:
- Add fullscreen modals for Direct Messages and Contact Management in index.html
- Configure FAB buttons to open modals instead of navigating to new pages
- Add JavaScript to reload iframe content when modals are opened (ensures fresh data)
- Remove DM and Contact Management from offcanvas menu to avoid duplication
- Both modals use iframes to embed existing /dm and /contacts/manage pages
- Modal headers styled with appropriate colors (green for DM, blue for Contacts)

This approach bypasses the viewport corruption issue since modals don't trigger
the problematic navigation flow that occurs with page changes in PWA mode.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-01 19:30:26 +01:00
MarekWo
82c606569a fix: Replace sticky bar with proper FAB buttons
Previous sticky bar implementation took up vertical space in layout,
making viewport issues worse. Replaced with proper floating action
buttons that don't affect layout.

Changes:
- Remove sticky bar from index.html
- Add FAB container with fixed position (no layout impact)
- Two circular buttons: DM (green) and Contacts (blue)
- position: fixed, right: 16px, top: 80px
- z-index: 900 (lower than offcanvas menu 1045)
- Beautiful gradients and shadows
- Hover/active animations (scale transform)
- Mobile responsive (smaller on <768px)

Features:
- No vertical space taken (fixed position)
- Covered by offcanvas when menu opens (z-index hierarchy)
- Always visible in corner
- Uses navigateTo() for clean navigation

Test scenario:
1. Click FAB buttons (bypass offcanvas menu completely)
2. Navigate to DM / Contact Management
3. Return to main page
4. Check if status bar corruption still occurs

If FABs work: problem is in offcanvas cleanup
If FABs fail: problem is deeper in viewport/layout

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-01 19:17:28 +01:00
MarekWo
b6499bfa55 test: Add floating navigation buttons to diagnose offcanvas issue
Added semi-transparent floating buttons under navbar to test
if viewport corruption is caused by offcanvas menu or navigation itself.

Changes:
- Add floating-test-buttons div in index.html
- Two buttons: DM and Contacts (using navigateTo())
- Sticky position under navbar
- Semi-transparent background with backdrop blur
- Centered layout with gap between buttons

Test scenario:
1. Click floating buttons (bypass offcanvas menu)
2. Navigate to DM / Contact Management
3. Return to main page via Home button
4. Check if status bar is still visible

If floating buttons work correctly:
- Problem is in offcanvas menu cleanup
- Can keep these buttons as permanent quick navigation

If floating buttons also cause corruption:
- Problem is deeper (viewport/layout issue)
- Need different approach

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-01 19:08:15 +01:00
MarekWo
bc4d29e369 fix: Add viewport recalculation on main page load (PWA fix)
The main page (app.js) was missing viewport recalculation logic
that was already present in dm.js, causing layout corruption when
returning from other pages in PWA mode.

Changes:
- Add viewport recalculation in DOMContentLoaded:
  - window.scrollTo(0, 0) - reset scroll position
  - window.dispatchEvent(new Event('resize')) - trigger reflow
  - document.body.offsetHeight - force layout recalculation

- Add pageshow event listener:
  - Handles page restoration from browser cache (Back button)
  - Recalculates viewport when event.persisted is true

- Add visibilitychange event listener:
  - Handles PWA returning from background
  - 100ms delay to ensure proper timing

This fixes the issue where:
1. User opens menu on main page
2. Navigates to Contact Management
3. Clicks Home button to return
4. Status bar is hidden under Android system buttons

The viewport wasn't being recalculated after navigation,
leaving the layout with incorrect heights from cached state.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-01 18:12:08 +01:00
MarekWo
11935d419a fix: Resolve PWA viewport corruption on page navigation
Root cause: Bootstrap offcanvas leaves backdrop/body classes in DOM
when navigating via window.location.href, causing viewport issues.

Changes:
- Remove hamburger menu button from DM navbar (caused overflow)
- Reduce menu button icon size on channels (remove font-size override)
- Add global navigateTo() function in app.js and contacts.js
- Function closes offcanvas, removes backdrops, clears body classes
- Replace all window.location.href calls with navigateTo()
- Updated navigation in: base.html, contacts*.html templates
- Add 100ms delay before navigation to ensure cleanup completes

This fixes the issue where:
1. Opening offcanvas menu on main page
2. Navigating to DM or Contact Management
3. Returning to main page
Results in corrupted viewport with hidden status bar

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-01 17:31:44 +01:00
MarekWo
00e512104c refactor: Completely unify channel and DM interface styling
Unify navbar:
- Add menu button to DM navbar (same icon as channels) for consistent height
- Both navbars now have 3 elements: back/brand, dropdown, menu button

Unify message input area:
- Disable textarea resize on both pages (resize: none)
- Unify input font-size on mobile (0.9rem)
- Unify form padding on mobile (0.5rem)
- Unify border-radius for send buttons

Unify message bubbles:
- Set DM message max-width to 70% (same as channels)
- Unify padding: 0.75rem 1rem (same as channels)
- Unify max-width on mobile: 85% for both
- Remove explicit font-size from DM bubbles (inherit default)
- Unify animation duration: 0.3s

Unify selectors:
- Apply same mobile styles to both #channelSelector and #dmConversationSelector

PWA fixes:
- Move safe-area handling from inline styles to main CSS
- Apply safe-area-inset-bottom globally for both pages
- Remove duplicate inline styles from dm.html

This should fix the Android PWA viewport issue where the bottom bar
gets hidden under system navigation after navigating to DM page.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-01 17:11:02 +01:00
MarekWo
ca2e2178c9 refactor: Unify channel and DM interface styling
- Change DM message input from input to textarea (rows=2) for consistency
- Increase DM form padding (p-2 → p-3) to match channel view
- Increase DM status bar padding (p-1 → p-2) with larger font
- Replace "Last refresh:" with "Updated:" in both views
- Update DM status to show connection states (Connected/Connecting/Disconnected) instead of "Ready"
- Add loadStatus() function to DM page for proper connection monitoring
- Unify message input area height and status bar appearance across both pages

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-01 16:35:59 +01:00
MarekWo
5518c622bc feat: Implement server-side read status for cross-device synchronization
Replace localStorage-based message read tracking with server-side storage
to enable unread badge synchronization across all devices and browsers.

Changes:
- Add read_status.py module for server-side read status management
- Add GET /api/read_status endpoint to fetch read status
- Add POST /api/read_status/mark_read endpoint to update read status
- Update app.js to load/save read status from server instead of localStorage
- Update dm.js to load/save DM read status from server instead of localStorage
- Read status stored in MC_CONFIG_DIR/.read_status.json for persistence

Benefits:
- Unread badges sync across all devices (phone, computer, tablet)
- Read status persists across browser sessions
- No more duplicate unread notifications when switching devices

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-01 15:41:41 +01:00
MarekWo
4345456db7 refactor(contacts): Remove path_len filter from cleanup tool
Remove impractical path length filter from contact cleanup feature.
The out_path_len parameter is rarely useful as most contacts have
value -1 (no route), making this filter unpractical for real-world use.

Changes:
- Remove path_len parameter from backend API endpoints
- Remove path length input field from HTML template
- Remove path_len collection from JavaScript code
- Update documentation (CLAUDE.md, README.md)
- Simplify cleanup filter to: name, types, date field, and days

Backend changes:
- Update _filter_contacts_by_criteria() to remove path_len logic
- Remove path_len validation from both endpoints
- Update API documentation in docstrings

Frontend changes:
- Remove Path Length input section from contacts-manage.html
- Remove path_len from collectCleanupCriteria() function

Documentation changes:
- Update API endpoint descriptions in CLAUDE.md
- Update cleanup instructions in README.md
- Remove path_len from example use cases

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-01 15:18:40 +01:00
MarekWo
cd65125b40 feat(contacts): Redesign cleanup tool with advanced filtering
Replace hours-based cleanup with multi-criteria contact filtering system:
- Add name filter (partial match, case-insensitive)
- Add contact type selection (CLI, REP, ROOM, SENS)
- Add date field selector (last_advert vs lastmod)
- Add days of inactivity filter (0 = ignore)
- Add path length filter (> X, 0 = ignore)
- Implement preview modal before deletion
- Support all contact types (CLI, REP, ROOM, SENS)

Backend changes:
- Add POST /api/contacts/preview-cleanup endpoint
- Refactor POST /api/contacts/cleanup with new filter logic
- Implement _filter_contacts_by_criteria helper function
- Use last_advert (reliable) instead of lastmod (unreliable)

Frontend changes:
- Move cleanup section below Existing Contacts
- Add collapsible Advanced Filters section
- Replace handleCleanupInactive with handleCleanupPreview/Confirm
- Add confirmation modal with contact list preview
- Display deletion results (success/failure counts)

Documentation updates:
- Update API endpoints in CLAUDE.md
- Update README.md with new cleanup instructions
- Remove deprecated MC_INACTIVE_HOURS from docs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-01 14:25:29 +01:00
MarekWo
5fc1c5b779 feat: Add mention badges, clickable URLs, and image previews to messages
Implemented comprehensive message content enhancements for both channel messages and DMs:

- **Mention badges**: @[Username] mentions now display as styled badges similar to Android Meshcore app
- **Clickable URLs**: http:// and https:// URLs are automatically converted to clickable links that open in new tabs
- **Image thumbnails**: URLs ending in .jpg, .jpeg, .png, .gif, .webp display as thumbnails with click-to-expand modal preview
- **Mobile responsive**: Image thumbnails adapt size for mobile screens
- **Security**: Proper XSS protection with HTML escaping and attribute sanitization

Technical implementation:
- Created shared message-utils.js module for content processing
- Added CSS styles for badges, links, images, and modal preview
- Updated both app.js and dm.js to use new content processor
- Used event delegation for image click handlers to support dynamic content
- Responsive design with mobile-optimized image sizes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-31 17:21:23 +01:00
MarekWo
61789d673c fix(contacts): Fix Pending Contacts list height
Problem: #pendingList had max-height: 200px showing only 1-2 contacts
with huge unused whitespace below.

Solution:
- Desktop: height calc(100vh - 280px) - slightly more than existingList
  due to extra description text
- Mobile: height calc(100vh - 320px) - adjusted for mobile layout
- Changed from max-height to height for consistent behavior
- Added min-height: 300px for safety

Now pending contacts list properly utilizes available screen space.
2025-12-30 21:25:15 +01:00
MarekWo
f8ef1ac297 docs: Move data storage to project directory and cleanup configuration
Major documentation update with new data structure:

Breaking Changes:
- Data storage moved from host directories to ./data/ inside project
- MC_CONFIG_DIR default changed: /root/.config/meshcore → ./data/meshcore
- MC_ARCHIVE_DIR default changed: /mnt/archive/meshcore → ./data/archive
- Requires migration for existing installations (see MIGRATION.md)

Documentation:
- Add MIGRATION.md - step-by-step guide for existing users
- Add FRESH_INSTALL.md - complete installation guide for new users
- Update README.md - new Configuration section with ./data/ structure
- Update .env.example - placeholders instead of real values, new defaults
- Update .claude/CLAUDE.md - updated environment variables documentation
- Change serial device detection from 'ls -l' to 'ls' (cleaner output)

Code Cleanup:
- Remove deprecated MC_REFRESH_INTERVAL variable (unused since intelligent refresh)
- Remove MC_REFRESH_INTERVAL from app/config.py
- Remove refresh_interval from app/routes/views.py (5 functions)
- Remove refresh_interval from app/routes/api.py
- Remove refreshInterval from app/templates/index.html
- Remove refreshInterval from app/templates/dm.html
- Remove MC_REFRESH_INTERVAL from docker-compose.yml

Configuration:
- Update .gitignore - exclude data/ and docs/github-discussion-*.md
- Serial port: use /dev/serial/by-id/[YOUR_DEVICE_ID] placeholder
- Device name: use [YOUR_DEVICE_NAME] placeholder

Benefits:
- All project data in one location (easier backups)
- Better portability (no host dependencies)
- Cleaner codebase (removed unused variables)
- Comprehensive documentation for migration and fresh install

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-30 15:31:26 +01:00
MarekWo
f8aedc5899 feat(contacts): Add Map button to open GPS location in Google Maps
Add Map button between Copy Key and Delete buttons on existing contacts cards.
Button appears only when contact has GPS coordinates (adv_lat/adv_lon != 0).
Opens contact location in Google Maps in new tab.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-30 12:43:50 +01:00
MarekWo
a53879df97 fix(ui): Move notifications to top-left corner and reduce display time
- Move toast container from bottom-right to top-left corner
- Reduce notification display time from 3-5s to 1.5s
- Prevents notifications from blocking message input area
- Applied consistently across all pages (main, DM, contacts)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-30 12:30:47 +01:00
MarekWo
34a86687de fix(contacts): Use fixed height instead of max-height to prevent page scroll
Problem: List was too tall causing the entire page to scroll,
hiding navigation buttons at the top when scrolling to bottom.

Solution:
- Changed from max-height to height for #existingList
- Desktop: calc(100vh - 260px) - slightly smaller to fit all elements
- Mobile: calc(100vh - 300px) - adjusted for mobile layout

Now only the contact list scrolls internally, while navigation
buttons remain visible at the top.
2025-12-30 12:18:30 +01:00
MarekWo
d8070d2bf4 fix(contacts): Fix CSS specificity conflict limiting list height
Problem: #existingList ID selector had higher specificity than
.contacts-list-fullscreen class, and max-height values were limiting
the list height regardless of the class height setting.

Changes:
- Desktop: #existingList max-height calc(100vh - 400px) → calc(100vh - 240px)
- Mobile: #existingList max-height calc(100vh - 450px) → calc(100vh - 280px)

This should now properly utilize available screen space.
2025-12-30 12:10:19 +01:00
MarekWo
3fbc4b7e2e fix(contacts): Further increase list height to eliminate bottom whitespace
Changed height calculation:
- Desktop: calc(100vh - 300px) → calc(100vh - 240px)
- Mobile: calc(100vh - 340px) → calc(100vh - 280px)

This should eliminate the ~20% unused whitespace at the bottom
of the contact lists.
2025-12-30 12:07:04 +01:00
MarekWo
ccff0cf465 fix(contacts): Improve full-screen list height calculation
Adjusted .contacts-list-fullscreen height to better utilize available
screen space by increasing the subtracted value from 200px to 300px
on desktop and from 150px to 340px on mobile.

This accounts for:
- Navbar (~56px)
- Main container padding (~48px)
- Back buttons (~60px)
- Search toolbar (~50px)
- Filter/sort toolbar (~60px)
- Margins between elements (~26px)

Fixes blank space at bottom of Existing Contacts and Pending Contacts pages.
2025-12-30 11:57:53 +01:00
MarekWo
10f5ce7b50 feat(contacts): Return full contact_info data from /api/contacts/detailed
Changed endpoint to return all fields from meshcli's contact_info command
instead of limited parsed data.

New fields available:
- public_key (full 64-char key)
- type (1=CLI, 2=REP, 3=ROOM, 4=SENS)
- flags
- out_path_len, out_path (routing information)
- last_advert (Unix timestamp)
- adv_lat, adv_lon (GPS coordinates)
- lastmod (last modification timestamp)

Kept compatibility fields:
- name (from adv_name)
- public_key_prefix (first 12 chars)
- type_label (human-readable)
- path_or_mode (computed from out_path_len/out_path)
- last_seen (alias for last_advert)

This enables future features like GPS mapping, path visualization, etc.
2025-12-30 10:09:34 +01:00
MarekWo
c0d61650a1 fix(contacts): Use contact name for deletion instead of public key
Testing revealed that meshcli's remove_contact command only works
with contact names, not with public keys (neither prefix nor full key).

Example test results:
- remove_contact bd030a71e091 → Unknown contact
- remove_contact bd030a71e091b14e...f7a → Unknown contact
- remove_contact "Progres-SLU-Lubsza-test" → SUCCESS

Changed frontend to send contact name as selector.
2025-12-30 09:56:58 +01:00
MarekWo
0d9fe53e53 fix(contacts): Use full public key for contact deletion
Problem:
- meshcli's remove_contact command requires full public key
- We were sending only 12-char prefix, causing 'Unknown contact' errors
- Contacts appeared in list but couldn't be deleted

Solution:
- API now includes full_public_key in /api/contacts/detailed response
- Frontend uses full_public_key when available, falls back to prefix
- Added detailed logging to track deletion attempts

This fixes the issue where contacts (especially with last_seen=4)
could not be removed from the device.
2025-12-30 09:44:09 +01:00
MarekWo
43edef22db debug(contacts): Add detailed logging for contact deletion
Added logging to see exactly what meshcli returns when removing a contact.
This will help diagnose why contacts are not being deleted despite
returning success status.
2025-12-30 09:37:18 +01:00
MarekWo
72605aed21 fix(ui): Add null check for cleanupBtn to prevent JS errors on main page
The cleanupBtn element only exists on the contact management page,
not on the main chat page. This was causing JavaScript to crash
with 'cannot access property addEventListener of null' error,
preventing messages from loading.

Added null check before attaching event listener.
2025-12-30 09:21:30 +01:00
MarekWo
c51a38aa1f fix(contacts): Fix scrolling and cleanup description display
- Add CSS override to enable scrolling on Contact Management pages
  (override global overflow: hidden from style.css)
- Move cleanup description to tooltip on info icon for consistency
  with Manual approval toggle pattern
- Add flexbox layout to cleanup section h6 for proper icon alignment

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-30 09:02:23 +01:00
MarekWo
cdc8be9eb4 refactor(contacts): Implement multi-page Contact Management with advanced sorting
Split Contact Management into 3 dedicated pages for improved mobile usability:
- /contacts/manage - Settings & navigation hub (manual approval + cleanup)
- /contacts/pending - Full-screen pending contacts view
- /contacts/existing - Full-screen existing contacts with search/filter/sort

New Features:
- Advanced sorting: Name (A-Z/Z-A) & Last advert (newest/oldest)
- URL-based sort state (?sort=name&order=asc)
- Activity indicators: 🟢 active, 🟡 recent, 🔴 inactive
- Changed terminology: "Last seen" → "Last advert" (more accurate)
- Cleanup tool moved from Settings modal to Contact Management page

Technical Changes:
- Created contacts_base.html standalone template
- Split contacts.html into 3 specialized templates
- Refactored contacts.js for multi-page support with page detection
- Added 2 new Flask routes: /contacts/pending, /contacts/existing
- Removed cleanup section from base.html Settings modal

Mobile-first design: Each page has full-screen space with touch-friendly
navigation and back buttons.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-30 08:40:22 +01:00
MarekWo
4349171acf cleanup(contacts): Remove debug logging from last_seen feature
Feature is now working correctly:
- Parses 263 contacts (17 CLI + 226 REP + 20 ROOM)
- Displays accurate last_seen timestamps with activity indicators
- Shows relative time ("52 minutes ago", "1 year ago")
- Color-coded status: 🟢 active, 🟡 recent, 🔴 inactive

Removed excessive debug logging as it's no longer needed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-29 13:27:56 +01:00
MarekWo
4c822e48e0 debug(contacts): Change to brace-matching JSON parser with output preview
Previous line-based parsing failed (0 contacts parsed despite receiving data).
New approach:
- Use brace-matching algorithm to find complete JSON objects {...}
- Works for both single-line and prettified (multi-line) JSON
- Added logging of first 500 chars of output for debugging

This handles the case where bridge may return prettified JSON instead
of newline-delimited JSON.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-29 13:22:38 +01:00
MarekWo
5c6b8570ab fix(contacts): Fix NDJSON parsing and use separate calls per contact type
Root cause: apply_to contact_info returns NDJSON (newline-delimited JSON),
not a JSON array. Each contact is a separate JSON object on its own line.

Changes:
- Call apply_to separately for each type (t=1, t=2, t=3, t=4) instead of
  using comma-separated list which returns 0 matches through bridge
- Parse NDJSON format: each line is a separate JSON object
- Skip non-JSON lines (prompt echoes "MarWoj|*", summary "> N matches")
- Collect all contacts from all types into single dictionary
- Add detailed logging for each type and total count

This matches the actual meshcli output format observed in interactive mode.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-29 13:18:48 +01:00
MarekWo
d49cfefca2 debug(contacts): Add detailed logging to diagnose last_seen matching issue
Add comprehensive debug logging to trace last_seen data flow:

cli.py (get_contacts_with_last_seen):
- Log apply_to command success/failure with error details
- Log number of bytes returned by command
- Log number of contacts parsed from JSON
- Log sample contact with public_key prefix and last_advert value
- Log raw output on JSON parse errors

api.py (get_contacts_detailed_api):
- Log number of detailed contacts retrieved
- Log number of matched contacts after merging
- Fix case sensitivity: normalize both prefix and full_key to lowercase

This will help identify why all contacts show "Last seen: Unknown".

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-29 13:11:21 +01:00
MarekWo
7f819b63c7 feat(contacts): Add 'Last Seen' timestamp display with activity indicators
Add comprehensive "last seen" tracking for all contact types:

Backend (cli.py):
- New function get_contacts_with_last_seen() using 'apply_to t=1,t=2,t=3,t=4 contact_info'
- Fetches detailed contact metadata including last_advert timestamps
- Returns dictionary indexed by full public_key for efficient lookup

API (api.py):
- Enhanced /api/contacts/detailed endpoint to merge last_seen data
- Matches contacts by public_key_prefix (first 12 chars)
- Graceful fallback if detailed fetch fails (contacts still displayed without last_seen)

Frontend (contacts.js):
- formatRelativeTime() - converts Unix timestamps to human-readable format
  ("5 minutes ago", "2 hours ago", "3 days ago")
- getActivityStatus() - returns status indicator based on recency:
  🟢 Active (< 5 min), 🟡 Recent (< 1 hour), 🔴 Inactive (> 1 hour)
- Contact cards now display "Last seen" with status icon and relative time
- Clean handling of missing last_seen data (shows "Unknown")

This feature helps users identify active vs. inactive contacts at a glance,
using the last_advert field from meshcli's contact_info command.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-29 13:03:15 +01:00
MarekWo
3e524bb0d2 refactor(ui): Optimize Contact Management layout for better space usage
Compact Manual Approval and Pending Contacts sections to give more
space for Existing Contacts list, which is the primary focus.

Changes to Manual Approval section:
- Convert from full section to single-line compact control
- Replace description text with tooltip icon (hover for info)
- Reduce vertical space by ~70px

Changes to Pending Contacts section:
- Reduce header size from h5 to h6
- Compact empty state (1rem padding vs 3rem)
- Reduce icon size (1.5rem vs 3rem)
- Limit list height to 200px (was 600px)
- Remove "Refresh" text from button (icon only)

Changes to Existing Contacts section:
- Dynamic height: calc(100vh - 400px) with 300px minimum
- Adapts to viewport height automatically
- On mobile: calc(100vh - 450px) for better fit
- More contacts visible without scrolling

Other improvements:
- Initialize Bootstrap tooltips in contacts.js
- Smaller fonts and margins throughout
- Better vertical space distribution

Result: ~150px more space for main contacts list on desktop,
~200px more on mobile. Tooltip provides same info without clutter.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-29 12:34:35 +01:00
MarekWo
7556136b82 fix(ui): Add scrollable container for contacts lists
Fix scrolling issue where contact lists with many items (263/350)
couldn't be scrolled on desktop or mobile devices.

Changes:
- Add max-height: 600px to #existingList and #pendingList
- Enable vertical scrolling with overflow-y: auto
- Add smooth scrolling for mobile with -webkit-overflow-scrolling
- Style custom scrollbar (8px, rounded, gray) for better UX

Tested: Lists now show ~8-10 contact cards at once with scrollbar
appearing when needed. Works with mouse wheel and touch gestures.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-29 12:18:43 +01:00
MarekWo
8b709b9136 feat(ui): Contact Management v2 - existing contacts display and delete
Implements MVP v2 requirements from docs/UI-Contact-Management-MVP-v2.md:
- Display all contact types (CLI, REP, ROOM, SENS)
- Delete contacts with confirmation modal
- Capacity counter with color-coded warnings (green/yellow/red)
- Search by name or public key
- Filter by contact type
- Mobile-first responsive design

Backend changes:
- Add get_all_contacts_detailed() parser for meshcli contacts output
  - Handles Unicode characters, emoji, spaces in names
  - Backward parsing strategy using public_key_prefix as anchor
  - Returns detailed metadata for all contact types
- Add delete_contact() wrapper for remove_contact command
- Add GET /api/contacts/detailed endpoint
- Add POST /api/contacts/delete endpoint

Frontend changes:
- Add Existing Contacts section to contacts.html
  - Real-time search input
  - Type filter dropdown (All/CLI/REP/ROOM/SENS)
  - Color-coded type badges
  - Capacity counter with pulse animation for critical levels
- Add delete confirmation modal with danger styling
- Add complete contact management logic to contacts.js
  - loadExistingContacts(), applyFilters(), confirmDelete()
  - Copy public key to clipboard functionality

Documentation:
- Update README.md with usage instructions
- Add technotes/UI-Contact-Management-MVP-v2-completed.md
- Add docs/UI-Contact-Management-MVP-v2.md (specification)
- Add technotes/UI-Contact-Management-MVP-v1-completed.md (retroactive)

Tested with 263 real contacts including Unicode and edge cases.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-29 11:45:47 +01:00
MarekWo
77c72ba62e feat: add contact management MVP (manual approval + pending)
Implements Contact Management UI with manual contact approval and pending contacts list.

**Backend changes (meshcore-bridge/bridge.py):**
- Remove forced manual_add_contacts on session init (was for testing only)
- Add _load_webui_settings() to read .webui_settings.json from MC_CONFIG_DIR
- Add /set_manual_add_contacts endpoint for persistent settings management
- Settings now persist across container restarts via .webui_settings.json

**Backend changes (app/meshcore/cli.py):**
- Add get_pending_contacts() - proxy to bridge /pending_contacts
- Add approve_pending_contact() - proxy to bridge /add_pending (always uses full public_key)
- Add get_device_settings() - read .webui_settings.json
- Add set_manual_add_contacts() - proxy to bridge /set_manual_add_contacts

**API changes (app/routes/api.py):**
- Add GET /api/contacts/pending - list pending contacts
- Add POST /api/contacts/pending/approve - approve contact by public_key
- Add GET /api/device/settings - get persistent settings
- Add POST /api/device/settings - update manual_add_contacts setting

**Frontend (app/routes/views.py, templates, js):**
- Add /contacts/manage route rendering contacts.html
- Add contacts.html template with mobile-first design
- Add contacts.js with settings toggle and pending list UI
- Add "Contact Management" menu item in base.html
- Features: manual approval toggle, pending list, approve/copy actions, toast notifications

**Documentation (README.md):**
- Add Contact Management section in Usage
- Add to Key Features list
- Add debugging instructions

**Key features:**
- Manual approval toggle (persists across restarts)
- Pending contacts list with name and public_key
- Approve button (always sends full public_key for compatibility)
- Copy full key button (clipboard API)
- Auto-refresh on page load
- Mobile-first responsive design
- Info badge when manual approval is disabled
- Toast notifications for user feedback

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-29 09:52:09 +01:00
MarekWo
ee7dde4ca2 fix(cli): Replace 'public' command with 'chan 0' to fix quote handling
Issue: Messages sent to Public channel had visible double quotes around
multi-word text (e.g., "Hello world" appeared as "Hello world" in chat).

Root cause: In interactive mode, meshcli's 'public' command treats quotes
literally as part of message content, while 'chan' command correctly parses
them as argument delimiters.

Solution: Use 'chan 0' for Public channel instead of 'public' command.
This ensures consistent quote handling across all channels.

Before:
- Public (ch 0): public "message" → quotes visible in output
- Other channels: chan <nb> "message" → quotes correctly parsed ✓

After:
- All channels: chan <nb> "message" → consistent behavior ✓

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 20:46:06 +01:00
MarekWo
9cd763c8c5 fix(dm): Remove DM button from messages and filter only CLI contacts
Two improvements to DM functionality:

1. Removed DM button from message blocks in channel view
   - Users should use the DM page directly instead
   - Cleaner UI without redundant buttons

2. Filter only CLI (client) contacts in DM dropdown
   - Added filter_types parameter to parse_contacts()
   - get_contacts_list() now returns only CLI contacts
   - Repeaters (REP), rooms (ROOM), and sensors (SENS) are excluded
   - You can't send DMs to repeaters anyway!

Updated README.md to reflect these changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 15:23:43 +01:00
MarekWo
2e009b2d3e refactor(dm): Show all contacts in DM dropdown selector
Changed DM approach from conditional button visibility to showing all
available contacts directly in the DM page dropdown. This provides better
UX and performance.

Changes:
- Reverted conditional DM button visibility in app.js (button always shows)
- Removed contacts loading from main page (app.js)
- Added loadContacts() function to dm.js to fetch contacts from API
- Modified populateConversationSelector() to show:
  1. Existing conversations (with history) first
  2. Separator: "--- Available contacts ---"
  3. All contacts from device who aren't in conversations yet
- Users can now start new DM conversations with any contact
- Updated README.md with new DM workflow description

Benefits:
- Simpler and more intuitive UX
- Better performance (no checks on every message)
- Users can proactively start conversations
- Clear visibility of who's available for DM

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 15:15:50 +01:00
MarekWo
94528c2a96 fix(parser): Fix contacts parsing to include emoji and spaces in names
Previous regex stopped at first space, causing names like "daniel5120 🔫"
to be parsed as just "daniel5120", breaking DM button visibility checks.

Changed parsing logic to split by 2+ consecutive spaces (column separator
in meshcli output) and extract full contact name before type column.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 14:59:23 +01:00
MarekWo
40a9b4e3bf feat: Conditional DM button visibility based on contacts list
The DM button is now only shown for users who are in the device's contacts
list, ensuring that direct messages will actually be delivered. This prevents
users from attempting to send DMs to recipients who cannot receive them.

Changes:
- Added parse_contacts() and get_contacts_list() functions to cli.py for parsing
  meshcli contacts output
- Created /api/contacts endpoint to retrieve contact names from device
- Modified frontend app.js to fetch and cache contacts list on page load
- Updated createMessageElement() to conditionally render DM button only when
  sender is in contacts list
- Updated README.md with note about DM button visibility requirement

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 14:31:14 +01:00