Commit Graph

45 Commits

Author SHA1 Message Date
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
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
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
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
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
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
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
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
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
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
MarekWo
fd014dfa4a fix(dm): Fix mobile menu link and page height issues - third attempt 2025-12-26 08:56:00 +01:00
MarekWo
2788b92687 fix(dm): Improve DM UI consistency and fix conversation merging
- Fix duplicate conversations in dropdown by merging pk_ and name_ IDs
- Add intelligent refresh (only reload when new messages arrive)
- Fix message alignment (own messages right, others left)
- Change sent message status from 'timeout' to 'pending'
- Add emoji picker button to DM page
- Change message limit from 200 to 140 bytes (consistent with channels)
- Update README.md with corrected DM documentation

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 22:29:57 +01:00
MarekWo
83b51ab2b9 refactor(dm): Replace modal with full-page view and fix sent DM tracking
- Replace DM modal with full-page view at /dm route for better mobile UX
- Add workaround for meshcore-cli bug where SENT_MSG contains sender's
  name instead of recipient - now saving sent DMs to separate log file
- Fix DM button styling to match Reply button (btn-outline-secondary)
- Add dm.js for DM page functionality
- Add dm.html template with green navbar for visual distinction
- Update menu link to navigate to /dm instead of opening modal
- Remove unused DM modal functions from app.js
- Update documentation with new DM workflow

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 20:27:02 +01:00
MarekWo
1d2cc7fe18 feat: Add direct messages (DM) support
- Parse PRIV (incoming) and SENT_MSG (outgoing) message types
- Add DM API endpoints: conversations, messages, updates
- Implement conversation grouping by pubkey_prefix or name
- Add timeout-based delivery status (pending → timeout)
- Add DM modal with conversation list and thread views
- Add dual notification badge (blue=channels, green=DM)
- Add DM button next to Reply on channel messages
- Include message deduplication for both incoming and outgoing

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 17:29:06 +01:00
MarekWo
80e9405449 feat: Add Network Commands section with advert and floodadv
Add new menu section "Network Commands" with two special commands:
- Send Advert: sends single advertisement (recommended for normal use)
- Flood Advert: floods network with advertisement (for recovery only)

Changes:
- cli.py: Add advert() and floodadv() functions
- api.py: Add POST /api/device/command and GET /api/device/commands endpoints
- base.html: Add Network Commands section to slide-out menu
- app.js: Add JavaScript handlers with confirmation for floodadv
- README.md: Document new Network Commands feature

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 11:27:17 +01:00
MarekWo
6c3551cd2d feat: Add support for public channels and auto-cleanup on channel deletion
- Allow joining public channels (starting with #) without encryption key
  - Frontend: Make key field optional with validation for # channels
  - Backend: Update API to accept optional key parameter
  - CLI wrapper: Build meshcli command dynamically based on key presence

- Implement automatic message cleanup when deleting channels
  - Add delete_channel_messages() function to remove channel history
  - Integrate cleanup into DELETE /api/channels endpoint
  - Prevents message leakage when reusing channel slots

- Update documentation with new features and usage instructions

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-24 12:34:14 +01:00
MarekWo
927d2a046f feat: Redesign UI for mobile with offcanvas menu and reduced message limit
- Change message byte limit from 200 to 140 bytes
- Remove keyboard hint text to save space
- Simplify navbar to show only notification bell and channel selector
- Add slide-out offcanvas menu for less-used options:
  * Refresh Messages
  * Manage Channels
  * Message History (archive selector)
  * Settings
- Increase font sizes in dropdowns and menu for better readability
- Add icons and text labels to all menu items
- Auto-close menu after selecting options
- Update clipboard API to modern navigator.clipboard with fallback

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-24 08:58:49 +01:00
MarekWo
a494f6b47d fix: Resolve channel dropdown disappearing issue
- Add timeout protection (10s for channels, 15s for updates)
- Implement fallback mechanism to ensure Public channel always exists
- Fix race condition by loading channels before setupAutoRefresh()
- Add proper validation for API responses and channel data
- Improve error handling with detailed logging
- Prevent checkForUpdates() from running before channels are loaded

This fixes the recurring issue where the channel dropdown would
randomly become empty, especially visible on mobile devices.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 21:44:34 +01:00
MarekWo
76134b133a feat: Implement intelligent refresh and multi-channel notifications
Replaces the blind 60-second refresh with a smart polling system that only updates the UI when new messages actually arrive.

Key improvements:
- Lightweight update checks every 10 seconds (vs full refresh every 60s)
- Chat view refreshes only when new messages appear on active channel
- Notification bell with global unread count across all channels
- Per-channel unread badges in channel selector (e.g., "Malopolska (3)")
- Last-seen timestamp tracking per channel with localStorage persistence
- Bell ring animation when new messages arrive

Backend changes:
- New /api/messages/updates endpoint for efficient update polling
- Returns per-channel update status and unread counts

Frontend changes:
- Smart auto-refresh mechanism with conditional UI updates
- Unread message tracking system with localStorage
- Notification bell UI component with badge
- Channel selector badges for unread messages
- CSS animations for bell ring effect

This dramatically reduces network traffic and server load while providing better UX through instant notifications about activity on other channels.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 18:12:53 +01:00
MarekWo
b223493183 feat: Add responsive design improvements for mobile devices
Improve UI layout on mobile devices (screens < 768px):

Navbar optimizations:
- Hide "Refresh" text on mobile, show only icon to save space
- Reduce gap between navbar elements (0.25rem instead of 0.5rem)
- Decrease min-width for channel/date selectors (80px vs 120px/150px)
- Smaller font-size (0.75rem) and padding for select elements
- Compact button sizes (0.25rem padding)

Modal optimizations:
- Reduce modal margins (0.5rem) for better mobile fit
- Smaller modal-body padding (0.75rem)
- Compact channel keys with smaller font (0.65rem) and word-break
- Reduce list-group-item padding (0.5rem)
- Stack "Add New" and "Join Existing" buttons vertically on very small screens (<400px)

Files changed:
- app/static/css/style.css: Added mobile-specific CSS rules in @media query
- app/templates/base.html: Wrapped "Refresh" text in <span class="btn-text">

Testing: Verified on mobile viewport (iPhone, Android simulators)

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 07:05:01 +01:00
MarekWo
b6e14dc291 fix: Resolve channel selector crash and cleanup
Fix critical JavaScript error that prevented channel selector from working:
- Add null check for selectedOption before accessing .text property
- Prevents "can't access property 'text', undefined" error at line 106
- This error occurred when selector options were being rebuilt

Additional cleanup:
- Remove duplicate escapeHtml() function definition
- Add detailed console logging to loadChannels() for debugging

Root cause: When populateChannelSelector() clears and rebuilds options,
the change event can fire with an invalid selectedIndex, causing
e.target.options[e.target.selectedIndex] to return undefined.

Bug reported by user: TypeError at app.js:106 when switching channels

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 19:32:02 +01:00
MarekWo
65e9f38f04 fix: Resolve channel selector dropdown not populating correctly
Fix issue where channel selector would show empty or missing channels
after auto-refresh. The populateChannelSelector() function was clearing
all options but not rebuilding them from API data correctly.

Changes:
- Rebuild entire dropdown from API response (including Public channel)
- Use currentChannelIdx from global state for selection
- Fallback to Public (index 0) if saved channel no longer exists
- Remove unused variable (IDE warning fix)

Bug: Channel selector showing empty after first auto-refresh
Root cause: API returns ALL channels including Public at index 0,
but the function was clearing all options and not adding them back.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 19:17:56 +01:00
MarekWo
761e4eac25 feat: Implement full channel management functionality
Add comprehensive channel management features to mc-webui:
- Create new channels with auto-generated encryption keys
- Share channels via QR code or copy-to-clipboard
- Join existing channels with name and key
- Switch between channels in chat interface
- Filter messages by channel
- Persistent channel selection (localStorage)

Backend changes:
- Add CLI wrapper functions: get_channels, add_channel, set_channel, remove_channel
- Modify send_message() to support channel targeting
- Parametrize parser channel filtering (channel_idx parameter)
- Add QR code generation with qrcode + Pillow libraries

API endpoints:
- GET /api/channels - List all channels
- POST /api/channels - Create new channel
- POST /api/channels/join - Join existing channel (auto-detect free slot)
- DELETE /api/channels/<index> - Remove channel
- GET /api/channels/<index>/qr - Generate QR code (JSON or PNG)
- Modified GET /api/messages - Add channel_idx filtering
- Modified POST /api/messages - Add channel_idx targeting

Frontend changes:
- Add channel selector dropdown in navbar
- Add Channels Management modal (create, join, list)
- Add Share Channel modal (QR code, copy key)
- Implement JavaScript channel management logic
- Add event handlers for channel switching
- Persist selected channel in localStorage

QR code format:
{"type":"meshcore_channel","name":"...","key":"..."}

Protection:
- Block deletion of Public channel (index 0)
- Validate channel names (alphanumeric, _, - only)
- Validate encryption keys (32 hex chars)
- Auto-detect free channel slots (1-7)

Backward compatibility:
- Default channel_idx=0 (Public) in all functions
- Existing Public-only code continues to work

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 19:04:00 +01:00
MarekWo
c6c345a6b6 Feature: Add favicon and PWA manifest for better branding
Added favicon support with multiple sizes for different platforms (16x16, 32x32, 180x180 for Apple, 192x192 and 512x512 for Android). Includes PWA manifest.json for standalone app display with Bootstrap primary theme color.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 09:53:32 +01:00
MarekWo
e879fec918 Feature: Add emoji picker for easy emoji insertion on desktop
Added a professional emoji picker widget to make it easier to insert emoji on desktop browsers where native emoji input is not readily available.

Changes:
- Added emoji-picker-element library from CDN (~50KB)
- New emoji button (😊) next to the Send button
- Full emoji picker with categories and search functionality
- Emoji inserted at cursor position in textarea
- Automatic byte counter update after insertion
- Mobile responsive: 6 columns on mobile, 8 on desktop
- Click outside to close picker

Benefits:
- Easy emoji access on Windows/Linux desktop browsers
- No need to use Win+. shortcut or copy-paste
- Professional UI with search and categories
- Still works with native emoji keyboards on mobile

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-21 20:57:44 +01:00
MarekWo
dd867c9fac Fix: Change message length counter from characters to UTF-8 bytes
Updated the message length validation to count actual UTF-8 bytes instead of Unicode characters. This ensures accurate enforcement of the 200-byte LoRa limit, preventing message truncation.

Changes:
- Frontend: Use TextEncoder to count UTF-8 bytes in real-time
- Backend: Validate byte length using text.encode('utf-8')
- HTML: Increased maxlength to 500 to allow more ASCII while JS enforces 200 bytes

Example: "zażółć gęślą jaźń 😀😁"
- Before: ~22 chars 
- After: ~35 bytes  (Polish chars = 2 bytes, emoji = 4 bytes each)

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-21 20:50:08 +01:00
MarekWo
f5fedbc96c Feature: Add message archiving system with browse-by-date selector
Implements automatic daily archiving of messages to improve performance
and enable browsing historical chat by date.

Backend changes:
- Add APScheduler for daily archiving at midnight (00:00 UTC)
- Create app/archiver/manager.py with archive logic and scheduler
- Extend parser.py to read from archive files and filter by days
- Add archive configuration to config.py (MC_ARCHIVE_*)

API changes:
- Extend GET /api/messages with archive_date and days parameters
- Add GET /api/archives endpoint to list available archives
- Add POST /api/archive/trigger for manual archiving

Frontend changes:
- Add date selector dropdown in navbar for archive browsing
- Implement archive list loading and date selection
- Update formatTime() to show full dates in archive view
- Live view now shows only last 7 days (configurable)

Docker & Config:
- Add archive volume mount in docker-compose.yml
- Add MC_ARCHIVE_DIR, MC_ARCHIVE_ENABLED, MC_ARCHIVE_RETENTION_DAYS env vars
- Update .env.example with archive configuration section

Documentation:
- Update README.md with archive feature and usage instructions
- Update .claude/instructions.md with archive endpoints

Key features:
- Automatic daily archiving (midnight UTC)
- Live view filtered to last 7 days for better performance
- Browse historical messages by date via dropdown selector
- Archives stored as dated files: {device}.YYYY-MM-DD.msgs
- Original .msgs file never modified (safe, read-only approach)

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-21 20:21:33 +01:00
MarekWo
8b36ff78bf Fix: Mobile viewport height issue - use dvh and improve flexbox
Fixed mobile browser viewport height issues where status bar and hint text were hidden:

CSS changes:
- Use 'height: 100dvh' (dynamic viewport height) for mobile browsers
- Fallback to '100vh' for older browsers
- Added 'min-height: 0' to main flex container (critical for flex children)

HTML changes:
- Added 'viewport-fit=cover' to meta tag (notched displays)
- Improved flexbox structure in index.html
- Added inline 'min-height: 0' on flex-grow-1 row (prevents overflow)

The 'dvh' unit dynamically adjusts to browser chrome (URL bar) visibility,
preventing layout shifts when scrolling on mobile. The min-height: 0 fix
ensures flex children properly shrink when needed.

This should fix the issue where bottom status bar disappears on mobile
and top navbar disappears after sending a message.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-21 17:10:11 +01:00
MarekWo
f361ebee09 Fix: Optimize UI for mobile devices
Improved mobile user experience:
- Shortened hint text: 'Shift+Enter: new line, Enter: send' (was 'Press Shift+Enter for new line, Enter to send')
- Reduced form padding on mobile (0.5rem instead of default)
- Made status bar more compact on mobile (smaller padding and font)
- Better vertical space utilization on small screens

This should make the status bar visible on mobile devices and improve overall usability on phones.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-21 16:59:37 +01:00
MarekWo
8d9c2b241c Feature: Add message length limit with live character counter
Implemented 200-character limit for messages due to LoRa/MeshCore constraints:
- Added maxlength=200 to textarea
- Added live character counter (0 / 200)
- Visual warnings: orange at 75%, red at 90%
- Counter updates on input, reply, and send
- Backend validation with descriptive error message
- Added technotes/limity.md documentation about MeshCore limits

Based on MeshCore LoRa payload constraints (~180-200 bytes safe limit).
This prevents message fragmentation and improves transmission reliability.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-21 14:44:41 +01:00
MarekWo
cf456422e2 Phase 1: Backend basics - Complete Flask application with REST API
Implemented core backend functionality:
- Flask application structure with blueprints
- Configuration module loading from environment variables
- MeshCore CLI wrapper with subprocess execution and timeout handling
- Message parser for .msgs JSON Lines file format
- REST API endpoints (messages, status, sync, contacts cleanup)
- HTML views with Bootstrap 5 responsive design
- Frontend JavaScript with auto-refresh and live updates
- Custom CSS styling for chat interface

API Endpoints:
- GET  /api/messages - List messages with pagination
- POST /api/messages - Send message with optional reply-to
- GET  /api/status - Device connection status
- POST /api/sync - Trigger message sync
- POST /api/contacts/cleanup - Remove inactive contacts
- GET  /api/device/info - Device information

Features:
- Auto-refresh every 60s (configurable)
- Reply to messages with @[UserName] format
- Toast notifications for feedback
- Settings modal for contact management
- Responsive design (mobile-friendly)
- Message bubbles with sender, timestamp, SNR, hop count

Ready for testing on production server (192.168.131.80:5000)

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

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