SQLite DELETE marks pages free but doesn't shrink the file, so the
new retention job would keep DBs at their bloated size forever without
a follow-up VACUUM. Add db.vacuum() that runs PRAGMA-free VACUUM and
reports size_before/size_after/elapsed so callers can surface results.
The retention job now calls vacuum() automatically when it deleted at
least 1000 rows. Threshold avoids the multi-second VACUUM cost on quiet
days. Failure is logged, not raised — a missed VACUUM never crashes
the scheduler.
Power-user override: new "Optimize now" button in the Database Backup
modal triggers VACUUM on demand via POST /api/db/vacuum, alongside a
GET /api/db/size that drives the live "Current size" label. This way
users don't have to wait until 03:30 to reclaim space after the first
big retention pass.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add a Settings > Analyzer tab letting users CRUD custom MeshCore Analyzer
services with a star-toggle default and inline disabled switch. The chart
icon under each group-chat message now resolves at click time: built-in
Letsmesh when no enabled customs, the default when set, or a chooser
modal otherwise. Backend stops shipping the prebuilt analyzer_url and
emits packet_hash instead — the frontend substitutes {packetHash} in the
chosen URL template.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The threshold above which the channel/DM list shows as a sidebar (vs.
collapsing to a top dropdown) is now user-configurable in
Settings -> Interface -> Layout. Persisted per device in LocalStorage
(key: mc-webui-sidebar-breakpoint, default: 992px, range: 600-2000).
Implementation: replaced hardcoded `@media (min-width: 992px)` with a
`.layout-wide` class on <html>, toggled by JS based on window.innerWidth
vs. the user's breakpoint. An inline script in <head> applies the class
synchronously to prevent layout flash on page load (same pattern as theme).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Lets users choose where each action appears: in the floating Quick
Access (FAB) bar or the slide-out Main Menu. Adds a "Hide Quick Access"
master switch and a per-item placement table in Settings -> Appearance.
Removes the stale "Refresh Messages" menu item (legacy of polling era,
WebSocket already covers it) and moves the Notifications toggle from
the menu to a dedicated Settings -> Notifications tab.
Each of the 11 configurable items is rendered in both locations;
applyItemPlacements() toggles d-none based on localStorage. Badges for
DM unread and pending contacts propagate to the Main Menu copies so
they stay in sync regardless of placement.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace the click-the-selected-radio-again gesture with a top-row
"None — use firmware default" radio, mirroring the per-channel region
picker. Users found the toggle gesture unintuitive; an explicit option
matches the picker pattern they already know.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Click the already-selected radio to clear the default; new
DELETE /api/regions/default endpoint also pushes an empty CMD 63 to
the firmware so its persistent default is wiped too.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two small follow-ups after initial deployment.
- Rename the Settings tab 'Channels' -> 'Regions' (id now tabSettingsRegions).
The tab manages the region registry, not channels; the old label was
confusing. The per-channel picker still lives under Manage Channels as
before.
- Graceful handling of firmware rejection: CMD_SET_DEFAULT_FLOOD_SCOPE
(63) and CMD_GET_DEFAULT_FLOOD_SCOPE (64) were introduced in firmware
v1.15.0; on v1.14.x the device replies with a generic ERR frame and
our toast showed the unhelpful 'Firmware error: unknown'. Now the
device_manager translates the empty/timeout reason into a concrete
message naming the v1.15 requirement, and the api handler appends
'Your choice is saved locally' so the user knows the local state
still persists. Same treatment for the delete-default-region clear
path.
Fourth slice — the feature is now functional end-to-end from UI to radio.
- Manage Channels modal: each row now has a pin-map button between Mute
and Share that opens a region picker for that channel; rows show an
inline badge with the assigned region name.
- Region picker modal (new #regionPickerModal): radio list of regions
with a "(None) — use firmware default" option at the top. Empty-state
shows a "Manage Regions" CTA that deep-links to Settings > Channels.
- api.py: two new routes —
- GET /api/channels/scopes → bulk map for UI rendering
- PUT /api/channels/<idx>/scope → {region_id: int | null} set/clear
- device_manager.send_channel_message: looks up the channel's scope,
then — under _send_lock — pushes the 16-byte key via CMD 54 before
the actual send_chan_msg. Channels without a mapping get an all-zero
key so a previously-set scope doesn't leak across channels (firmware's
send_scope is sticky until overwritten, not one-shot).
Third slice — users can now curate their device-wide region list. No
per-channel mapping yet; that's PR #4.
- base.html: new Channels tab in the Settings modal with an info banner
pointing at regions.meshcore.nz, the list container, and an add-region
form.
- app.js: loadRegions / addRegion / deleteRegion / setDefaultRegion
mirroring the loadContactsSettings / saveContactsSetting pattern. Client
-side name validation (isValidRegionName) mirrors the firmware
RegionMap::is_name_char byte-rule exactly so users get instant feedback
on invalid chars without a round-trip.
- api.py: four routes under /api/regions —
- GET /api/regions → list registry
- POST /api/regions {name} → derive key + insert; 409 dup
- DELETE /api/regions/<id> → cascade channel mappings; if
the deleted region was firmware default, best-effort clear on device
- POST /api/regions/<id>/default → flip DB flag + push CMD 63;
if firmware push fails, DB still flips and response includes a
non-blocking `warning` for a toast
Reduce wrapper min-width 100px → 80px and tighten the form-select
chevron padding (2rem → 1.5rem right, 0.6rem → 0.5rem left) so the
navbar fits in one row on ~360px-wide phones while keeping the
chevron and text both visible.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Reduce wrapper min-width from 140px to 100px (with 140px max-width) so
the bell + channel selector + menu button all fit in one navbar row on
~412px-wide phones, without sacrificing readable channel names when
there's room to grow.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace the native <select> channel picker (used on narrow screens) with
a custom searchable dropdown matching the DM contact picker UX: type to
filter, arrow/Enter keyboard nav, click-outside to close, per-channel
unread badges, muted styling. Wide-screen sidebar (lg+) is unchanged.
Also align the DM picker dropdown font with the wide-screen DM sidebar
(0.88rem / weight 400) — was inheriting larger/bolder from form-control.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a Path hash mode dropdown (1B/2B/3B) to Settings → Device → Public
Info, so the mode can be switched from the UI instead of the meshcli
console. The Settings modal now has a persistent Close button in the
footer, visible on every tab.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Move Manual approval toggle into a new Contacts tab in the global
Settings modal and clean up the Contact Management panel (drop the
duplicated Settings/Manage Contacts headers, shorten the Existing
Contacts blurb). Add two new persisted options gated on Manual
approval being ON: Suppress new advert notifications (frontend hides
FAB badge + browser notification while the Pending list itself stays
populated) and Automatically add new contacts to "Ignored" (advert
handler marks the new contact ignored before emitting pending_contact,
so the user is silenced end-to-end while contacts remain in the cache
for promotion via "To Device").
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Users complained that the route popup under group-chat messages and the
top-of-page notification toasts auto-close before they can read them, and
some users wanted to move the toasts out of the top-left corner.
Adds to Settings modal:
- Group Chat tab: route popup auto-close timeout + "don't close" switch
(applies to both channel popups and DM route popups)
- New Interface tab: toast auto-close timeout, "don't close" switch, and
five position options (top-left/top-right/bottom-left/bottom-right/center)
Persisted as chat_settings (extended) and a new ui_settings row in the
app_settings table, with /api/chat/settings and /api/ui/settings endpoints.
Default toast delay bumped from 1.5s to 2s.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a Device tab as the first tab in the Settings modal with two sub-tabs:
- Public Info: device name, coordinates with map picker, advert location sharing
- Radio Settings: frequency, bandwidth, SF, CR, TX power with region presets
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add Settings quick-access button to both main chat and DM views
- Make FAB container draggable via toggle button with position saved to localStorage
- Add button size and spacing sliders in Settings > Appearance tab
- Use CSS custom properties for dynamic FAB sizing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Integrate meshcore library's BLE connection (via bleak) as a third
transport option alongside serial and TCP. Priority: BLE > TCP > Serial.
Config: MC_BLE_ADDRESS and MC_BLE_PIN environment variables.
Docker: bluez/dbus packages, NET_ADMIN cap, D-Bus socket mount.
UI: transport type badge in navbar, transport_type in /api/status.
Watchdog: skip USB reset for BLE connections (same as TCP).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace 3-way branching (configured_paths/has_path/else) with
4-scenario matrix based on (has_path × has_configured_paths):
- S1: No path, no configured paths → FLOOD only
- S2: Has path, no configured paths → DIRECT + optional FLOOD
- S3: No path, has configured paths → FLOOD first, then ŚD rotation
- S4: Has path, has configured paths → DIRECT on ŚK, ŚD rotation, optional FLOOD
Key changes:
- S3: FLOOD before configured paths (discover new routes)
- S4: exhaust retries on current ŚK before rotating ŚD
- S4: dedup ŚG/ŚK to skip redundant retries on same path
- Add _paths_match() helper for path deduplication
- Update tooltip text for settings clarity
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create theme.css with CSS custom properties for light/dark themes
- Dark theme inspired by demo landing page (deep navy palette)
- Update style.css: replace ~145 hardcoded colors with CSS variables
- Extract inline styles from index.html, contacts.html, dm.html to style.css
- Add Appearance tab in Settings modal with theme selector
- Bootstrap 5.3 data-bs-theme integration for native dark mode
- Theme persisted in localStorage, applied before CSS loads (no FOUC)
- Console and System Log panels unchanged (already dark themed)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a Share tab to the Device Info modal that generates a QR code
and copyable URI (meshcore://contact/add?...) for sharing the device
contact with other users.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Group Chat tab in Settings with configurable quote byte limit.
When quoting a message longer than the limit, a dialog asks whether
to use full or truncated quote (with editable byte count).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The MeshCore community uses "companion" not "client" for type 1 nodes.
Rename the CLI label to COM across all UI, API, JS, and docs to align
with official terminology. Includes cache migration for old CLI entries.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace vertical form fields with table rows for less screen space.
Descriptions moved to (i) tooltip icons on hover/touch.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace hardcoded DM retry logic with user-configurable settings stored
in app_settings DB. Settings modal opens from menu with tab-based UI
(ready for future settings tabs). Defaults: 3 direct + 1 flood retries
(was 8+2), 30s/60s intervals, 60s grace period.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reorganize menu from 2 sections (Network Commands, Configuration) into 4:
- Messages (top, no header) - daily actions
- Network - advert commands
- Tools - Map, Console
- System - Device Info, System Log, Backup, Settings placeholder
Increase navbar button/select touch targets (min 40px) for mobile usability.
Widen offcanvas menu from 280px to 300px.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Changed from target="_blank" link to fullscreen modal with iframe,
matching the pattern used by Console, DM, and Contacts modals.
Iframe loads on open and clears on close to manage WebSocket lifecycle.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
In-memory ring buffer (2000 entries) captures all Python log records.
New /logs page streams entries via WebSocket in real-time with:
- Level filter (DEBUG/INFO/WARNING/ERROR)
- Module filter (auto-populated from seen loggers)
- Text search with highlighting
- Auto-scroll with pause/resume
- Dark theme matching Console style
Menu entry added under Configuration section.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Channel names from device already include # prefix — removed hardcoded #
from search results badge. Added (?) help button with search syntax
examples and link to FTS5 docs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Search, backup, stats endpoints used current_app.config.get('DEVICE_MANAGER')
which doesn't exist — replaced with _get_dm()/_get_db() helpers
- /api/device/info used old v1 CLI — replaced with DeviceManager.get_device_info()
returning structured dict instead of string (fixes map own device marker)
- Moved search button from navbar to FAB menu (between filter and DM buttons)
- Bump SW cache to v7
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New GET /api/device/stats endpoint (core, radio, packets, DB stats)
- Device Info modal now has Info and Stats tabs
- Stats tab shows: battery, uptime, TX/RX air time, packet counts,
DB row counts, and database size
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- POST /api/backup/create — trigger immediate backup
- GET /api/backup/list — list backups with sizes
- GET /api/backup/download — download backup file
- Backup modal accessible from menu with create/download buttons
- Daily automatic backup via APScheduler (configurable hour/retention)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New GET /api/messages/search endpoint using existing FTS5 indexes
- Search modal accessible from navbar search icon
- Debounced search (300ms) across all channel and DM messages
- Results show source (channel/DM), sender, timestamp with highlights
- Click result navigates to the relevant channel or DM conversation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Map modal (from main menu) now has a "Cached" toggle switch that,
when enabled, also displays contacts stored in the DB cache alongside
device contacts. Cached markers are slightly smaller and more
transparent to visually distinguish them from device contacts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add SocketIO /chat client to app.js for real-time message updates
- Listen for new_message (channel) events, refresh current channel
- Update unread badges for other channels in real-time
- Listen for device_status to update connection indicator
- Reduce polling interval from 10s to 60s (fallback only)
- Include socket.io.min.js in base.html template
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add ability to mute notifications for individual channels via Manage
Channels modal (bell/bell-slash toggle button). Muted channels are
excluded from unread badge counts, browser notifications, and app icon
badge. Bell icon click now marks all channels as read in bulk.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add filter-utils.js with diacritic-insensitive search and text highlighting
- Add FAB filter button (gray funnel icon) to channel chat and DM
- Filter bar slides in as overlay at top of chat area
- Real-time filtering with debounce (150ms) as user types
- Matched text highlighted with yellow background
- Support for Polish characters (wol matches wół)
- Keyboard shortcuts: Ctrl+F to open, Escape to close
- Match counter shows X / Y filtered messages
- Filter persists during auto-refresh
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Convert type filter checkboxes to clickable toggle badges on the Map
modal (accessible from main menu). Same UX pattern as pending contacts:
- Active: colored background, white text
- Inactive: colored text/border, white background
Updated in both base.html and contacts_base.html for consistency.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add sudo to journalctl command in install.sh help text
- Move "Update now" link below version number to prevent line wrap
- Add "What's new?" link in update modal pointing to GitHub commits
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Git needs HOME environment variable to find ~/.gitconfig with
safe.directory setting when running as root. Also reverts TEST2 marker.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add -u flag to Python for unbuffered logging to journald
- Configure git safe.directory automatically during install
- Revert test marker from base.html
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds webhook-based update system that allows triggering updates
directly from the mc-webui menu. Includes:
- Webhook server (updater.py) on port 5050
- Systemd service and install script
- API proxy endpoints for container-to-host communication
- Update modal with progress tracking and auto-reload
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- version.py now captures and exports GIT_BRANCH
- Display branch badge next to version in menu (e.g., "2026.01.20+abc1234 [dev]")
- /api/version now returns branch field
- /api/check-update uses frozen branch instead of hardcoded "dev"
- Allows proper update checking for both dev and main branches
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add /api/check-update endpoint that queries GitHub API
- Compare current commit hash with latest on dev branch
- Add check button next to version in menu
- Show spinning icon during check, green checkmark when done
- Display "Update available" link when newer version exists
- Handle rate limits and network errors gracefully
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Rename Settings to Device Info with new cpu icon
- Display device parameters in readable table format
- Add copy-to-clipboard buttons for Name and Public Key
- Add map button for device location coordinates
- Show human-readable parameter names and values
- Hide telemetry parameters (not commonly needed)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>