mirror of
https://github.com/pe1hvh/meshcore-gui.git
synced 2026-11-05 20:12:38 +01:00
WHAT - route_page: _default_reply_channel() derives the reply select default from msg.channel instead of the first entry in the channel list - models: new Message.date field, now_date() and display_timestamp(); format_line() and incoming()/outgoing() use them - message_archive: persist "date" alongside "time" and "timestamp_utc" - room_server_panel + route_page info header use display_timestamp() - VERSION 1.22.2 -> 1.22.3 WHY The route page exists to show one specific message, so that message — not the ordering of the channel list — is the correct source for the reply default. The date could not be fixed at the render site because Message had no date at all: now_timestamp() yields HH:MM:SS and the date lived only as timestamp_utc inside the archive layer, unread by from_dict(). A first-class field plus a single formatting helper fixes the cause and keeps the format in one place. NOTES No breaking changes. The archive schema gains one additive field; pre-1.22.3 entries have their date derived from timestamp_utc (converted to local time) and fall back to time-only when absent. Message.incoming() gained an optional keyword-only date parameter. Message display lines are ~11 characters wider.
215 lines
3.9 KiB
Markdown
215 lines
3.9 KiB
Markdown
# Map Architecture — MeshCore GUI
|
|
|
|
## Overview
|
|
|
|
The MeshCore GUI map subsystem is implemented as a **browser-managed Leaflet runtime** embedded inside a NiceGUI container.
|
|
|
|
The key design decision is that the **map lifecycle is owned by the browser**, not by the Python UI update loop.
|
|
|
|
NiceGUI acts only as a container and data provider.
|
|
|
|
This architecture prevents map resets, marker flicker, and viewport jumps during the 500 ms dashboard refresh cycle.
|
|
|
|
The rationale and binding rules for this approach are recorded in **`docs/adr/ADR-001-browser-managed-leaflet.md`**.
|
|
|
|
---
|
|
|
|
# Architecture
|
|
|
|
```
|
|
NiceGUI Dashboard
|
|
│
|
|
│ snapshot (500 ms)
|
|
▼
|
|
MapPanel (Python)
|
|
│
|
|
│ JSON payload
|
|
▼
|
|
Leaflet Runtime (Browser)
|
|
│
|
|
├─ Map instance (persistent)
|
|
├─ Marker registry
|
|
├─ Contact cluster layer
|
|
├─ Theme state
|
|
└─ Viewport state
|
|
```
|
|
|
|
---
|
|
|
|
# Component Responsibilities
|
|
|
|
## MapPanel (Python)
|
|
|
|
Location:
|
|
|
|
```
|
|
meshcore_gui/gui/panels/map_panel.py
|
|
```
|
|
|
|
Responsibilities:
|
|
|
|
* provides the map container
|
|
* injects the Leaflet runtime assets
|
|
* sends compact map snapshots
|
|
* handles UI actions:
|
|
|
|
* theme toggle
|
|
* center on device
|
|
|
|
MapPanel **does NOT control the Leaflet map directly**.
|
|
|
|
It only sends data.
|
|
|
|
---
|
|
|
|
## MapSnapshotService
|
|
|
|
Location:
|
|
|
|
```
|
|
meshcore_gui/services/map_snapshot_service.py
|
|
```
|
|
|
|
Responsibilities:
|
|
|
|
* converts device/contact data into a compact JSON snapshot
|
|
* ensures stable node identifiers
|
|
* prepares payloads for the browser runtime
|
|
|
|
Example snapshot structure:
|
|
|
|
```json
|
|
{
|
|
"device": {...},
|
|
"contacts": [...],
|
|
"force_center": false
|
|
}
|
|
```
|
|
|
|
Snapshots are emitted every **500 ms** by the dashboard update loop.
|
|
|
|
---
|
|
|
|
## Leaflet Runtime
|
|
|
|
Location:
|
|
|
|
```
|
|
meshcore_gui/static/leaflet_map_panel.js
|
|
```
|
|
|
|
Responsibilities:
|
|
|
|
* initialize the Leaflet map once
|
|
* maintain persistent map instance
|
|
* manage marker registry
|
|
* maintain a persistent contact cluster layer
|
|
* keep the own-device marker outside clustering
|
|
* apply snapshots incrementally
|
|
* manage map theme and viewport state
|
|
|
|
Key design rules:
|
|
|
|
```
|
|
map is created once
|
|
markers updated incrementally
|
|
snapshots never recreate the map
|
|
clustering is attached only after maxZoom is known
|
|
```
|
|
|
|
---
|
|
|
|
# Update Flow
|
|
|
|
```
|
|
SharedData
|
|
│
|
|
▼
|
|
Dashboard update loop (500 ms)
|
|
│
|
|
▼
|
|
MapSnapshotService
|
|
│
|
|
▼
|
|
MapPanel
|
|
│
|
|
▼
|
|
Leaflet Runtime
|
|
```
|
|
|
|
Snapshots are **coalesced** so the browser applies only the newest payload.
|
|
|
|
---
|
|
|
|
# Theme Handling
|
|
|
|
Theme changes are handled via a **dedicated theme channel**.
|
|
|
|
Snapshots do **not** carry theme information.
|
|
|
|
Reason:
|
|
|
|
Embedding theme state in snapshots caused race conditions where queued snapshots overwrote explicit user selections.
|
|
|
|
Theme state is managed in the browser runtime and restored on reconnect.
|
|
|
|
---
|
|
|
|
# Marker Model
|
|
|
|
Markers are keyed by **stable node id**.
|
|
|
|
```
|
|
device marker (standalone)
|
|
contact markers (clustered)
|
|
```
|
|
|
|
Updates are applied incrementally:
|
|
|
|
```
|
|
add marker
|
|
update marker
|
|
remove marker
|
|
```
|
|
|
|
This prevents marker flicker during the refresh loop.
|
|
|
|
---
|
|
|
|
# Reconnect Behaviour
|
|
|
|
When the NiceGUI connection temporarily drops:
|
|
|
|
1. the Leaflet runtime persists in the browser
|
|
2. the map instance remains intact
|
|
3. theme and viewport state are restored
|
|
4. snapshot updates resume once the connection returns
|
|
|
|
---
|
|
|
|
# Future Extensions
|
|
|
|
Possible improvements without breaking the architecture:
|
|
|
|
* heatmap layers
|
|
* route overlays
|
|
* tile provider switching
|
|
* richer cluster icons or spiderfy tuning
|
|
|
|
All extensions must remain **browser-managed**.
|
|
|
|
---
|
|
|
|
# Summary
|
|
|
|
The MeshCore map subsystem follows a strict separation:
|
|
|
|
```
|
|
Python → data
|
|
Browser → map lifecycle
|
|
```
|
|
|
|
This prevents UI refresh cycles from interfering with map state and ensures smooth rendering even with frequent dashboard updates.
|
|
|
|
Binding constraints (what developers must and must not do) are listed in **`docs/adr/ADR-001-browser-managed-leaflet.md`** under "Consequences → Bindende uitvloeisels".
|