`. NiceGUI/Quasar does not include Tailwind CSS,
+ so `h-96` had no effect and the container rendered at height 0. Leaflet initialized
+ on a zero-height element and produced a blank map.
+- 🛠**Route map not rendered when no node has GPS coordinates** — `_render_map`
+ returned early before creating the Leaflet container when `payload['nodes']` was
+ empty. Fixed: container is always created; a notice label is shown instead.
### Changed
+- 🔄 `meshcore_gui/static/leaflet_map_panel.js` — Added size guard in `ensureMap`:
+ returns `null` when host has `clientWidth === 0 && clientHeight === 0` and no map
+ state exists yet. `processPending` retries on the next tick once the panel is visible.
- 🔄 `meshcore_gui/gui/dashboard.py` — Consolidated two conditional map-update blocks
- into a single unconditional `self._map.update(data)` that fires on every timer tick
- while the MAP panel is active. The JS runtime coalesces pending payloads so only the
- newest snapshot is ever applied; the extra calls are cheap.
-- 🔄 `meshcore_gui/gui/route_page.py` — Removed early `return` from `_render_map` when
- no GPS nodes are present. The Leaflet container is now always created. A small inline
- notice is shown when there is no location data instead of skipping the map entirely.
+ into a single unconditional update while the MAP panel is active. Added `h-96` to the
+ DOMCA CSS height overrides for consistency with the route page map container.
+- 🔄 `meshcore_gui/gui/route_page.py` — Replaced `h-96` Tailwind class on the route
+ map host `
` with an explicit inline `style` (height: 24rem). Removed early
+ `return` guard so the Leaflet container is always created.
### Impact
- MAP panel now renders reliably on first open regardless of contact/GPS availability
-- Route map now always shows even when route nodes carry no GPS coordinates
-- No breaking changes — only the two files above are modified
+- Route map now always shows with correct height even when route nodes have no GPS
+- No breaking changes outside the three files listed above
---
## [1.13.1] - 2026-03-09 — Message Icon Consistency
diff --git a/meshcore_gui/gui/dashboard.py b/meshcore_gui/gui/dashboard.py
index e037aa2..6f86e63 100644
--- a/meshcore_gui/gui/dashboard.py
+++ b/meshcore_gui/gui/dashboard.py
@@ -193,6 +193,7 @@ body.body--light .domca-drawer .q-item { color: #3d6380 !important; }
.domca-panel .h-40 { height: calc(100vh - 20rem) !important; min-height: 10rem; }
.domca-panel .h-32 { height: calc(100vh - 24rem) !important; min-height: 8rem; }
.domca-panel .h-72 { height: calc(100vh - 12rem) !important; min-height: 14rem; }
+.domca-panel .h-96 { height: calc(100vh - 8rem) !important; min-height: 16rem; }
.domca-panel .max-h-48 { max-height: calc(100vh - 16rem) !important; min-height: 6rem; }
/* ── Allow narrow viewports down to 320px ── */
diff --git a/meshcore_gui/gui/route_page.py b/meshcore_gui/gui/route_page.py
index 9377866..492346a 100644
--- a/meshcore_gui/gui/route_page.py
+++ b/meshcore_gui/gui/route_page.py
@@ -251,8 +251,8 @@ class RoutePage:
container_id = f'route-map-{uuid4().hex}'
ui.html(
- f'
'
- ).classes('w-full')
+ f'
'
+ ).classes('w-full').style('height: 24rem')
boot_script = (
'(function bootRouteMap(retries){'
diff --git a/meshcore_gui/static/leaflet_map_panel.js b/meshcore_gui/static/leaflet_map_panel.js
index 63dc0b0..63c5979 100644
--- a/meshcore_gui/static/leaflet_map_panel.js
+++ b/meshcore_gui/static/leaflet_map_panel.js
@@ -42,6 +42,16 @@
return null;
}
+ // Do not initialize the Leaflet map while the host container has no
+ // rendered dimensions. This happens when the map panel is hidden at
+ // page load (display:none via Vue v-show). Calling L.map() on a
+ // zero-size element produces a broken map that never recovers.
+ // processPending will retry on the next scheduled tick once the panel
+ // becomes visible and the host gains real dimensions.
+ if (!existing && host.clientWidth === 0 && host.clientHeight === 0) {
+ return null;
+ }
+
if (existing) {
if (existing.host !== host) {
if (existing.resizeObserver) {