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>
Remove workaround for meshcore-cli 1.3.11 bug where SENT_MSG contained
sender name instead of recipient. Now using native SENT_MSG entries from
.msgs file with correct recipient and sender fields (requires meshcore-cli >= 1.3.12).
Changes:
- Add _parse_sent_msg() to parse SENT_MSG from .msgs file
- Update read_dm_messages() to process both PRIV and SENT_MSG from .msgs
- Remove save_sent_dm(), _read_sent_dm_log(), _parse_sent_dm_entry()
- Remove dm_sent_log_path property from config
- Add _cleanup_old_dm_sent_log() to remove obsolete log file
- Update comments and documentation
Benefits:
- Single source of truth (.msgs file only)
- Simpler codebase (-95 lines)
- No custom workarounds
- Better data consistency
Related: meshcore-cli update to 1.3.12 (commit ad4a7b3)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- 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>
- 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>
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>
- 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>
Root cause: Multiple concurrent meshcli calls were fighting for USB access,
causing "Protocol error" and 504 Gateway Timeouts.
Changes to meshcore-bridge:
- Add threading.Lock to serialize meshcli subprocess calls
- Prevent concurrent USB access that causes OSError [Errno 71]
- Reduce DEFAULT_TIMEOUT from 30s to 10s
- Add detailed logging for lock acquisition and release
Changes to main API:
- Implement 30s cache for get_channels() to reduce USB calls
- Cache invalidation after channel create/join/delete operations
- Use cached channels in /api/channels and /api/messages/updates
- Reduce HTTP timeout from 30s to 12s (10s bridge + 2s buffer)
Impact:
- Eliminates race conditions when page loads (multiple API calls)
- Prevents USB port conflicts and protocol errors
- Faster response times due to caching
- No need for manual USB resets after container restarts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
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>
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>
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>
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>