From 0c263fb6018d827e71f89e2c399829141110cc02 Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Tue, 29 Sep 2026 22:45:32 +0000 Subject: [PATCH] A research map released mid-start no longer reports a failed map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the reader switches tab while the research map is still starting, the gallery releases the pane, and initializeMap returns quietly with no map. The handler logged that as "Map failed to initialize — check token and container", so the console error landed in whichever tab was then open and blamed the token. A released container is now a quiet cancel. --- plugin-designs/doxa-research-map/research-map.vue | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plugin-designs/doxa-research-map/research-map.vue b/plugin-designs/doxa-research-map/research-map.vue index 74bd0eff8..e593682e4 100644 --- a/plugin-designs/doxa-research-map/research-map.vue +++ b/plugin-designs/doxa-research-map/research-map.vue @@ -3035,14 +3035,23 @@ onMounted(async () => { console.error('[research-map] data load failed:', err) return { normalizedPeopleGroups: [] } }) + // ★ A PANE RELEASED WHILE ITS MAP IS STILL STARTING IS A CANCEL, NOT A FAILURE. When the reader + // switches tab, the gallery releases this element. initializeMap then returns SILENTLY (see + // useMapInstance's liveness checks) and leaves map.value null. This handler reported that as "Map + // failed to initialize — check token and container", so a console ERROR landed in whichever tab was + // open by then and blamed the token for a tab switch (measured 2026-09-29, a phone in Arabic, + // 2 runs in 3). The pane is gone, so nobody is waiting for a map: return quietly. + const released = () => !mapContainer.value || !mapContainer.value.isConnected try { await initializeMap() } catch (err) { + if (released()) return // `new mapboxgl.Map()` throws outright when the device has no WebGL context. console.error('[research-map] Map failed to initialize —', err) mapError.value = String(err?.message || err) return } + if (released()) return // Keep the Mapbox Map OUT of Vue's reactive Proxy. useMapInstance holds it in a plain // ref() (deep-reactive): every internal read/write inside Mapbox then pays the Proxy // get/set trap, and a render scheduled through the Proxy (any map.value.* call →