diff --git a/plugin-designs/gospel-internet/src/boardEngine.js b/plugin-designs/gospel-internet/src/boardEngine.js deleted file mode 100644 index 6f5376f1b..000000000 --- a/plugin-designs/gospel-internet/src/boardEngine.js +++ /dev/null @@ -1,67 +0,0 @@ -/** - * boardEngine.js — build one map on whichever engine the embed asked for, the way this board builds all - * of its maps: engine from resolveMapProvider, script + CSS ensured, RTL shaping installed before `new - * Map`, the style from the registry ('paper' = Mapbox light / OpenFreeMap positron), labels in the - * reader's language, attribution added. Used by the geographic map AND the language-tree whiteboard, so - * the two can never drift apart on an engine detail. - * - * whiteboard:true hides every basemap layer except the background once the style loads. The style is - * still loaded because it is what declares the GLYPH server: a hand-typed glyph URL is how labels - * silently vanished on one engine before (library/maps/label-font.js). - */ -import { resolveMapProvider } from '@map/composables/useMapProvider.js' -import { ensureEngineScript, engineGlobal, ensureEngineCSS, engineStylesheetUrl, engineUiLocale } from '@map/composables/useMapInstance.js' -import { resolveNamedStyle } from '@map/styles/registry.js' -import { installRtlTextPlugin } from '@map/maps/rtl-text.js' -import { localizeBasemapLabels } from '@map/maps/localize-labels.js' -import { addMapAttribution } from '@map/composables/useMapAttribution' - -export function readerLang(profileConfig) { - const pc = profileConfig || {} - if (pc.locale || pc.lang) return String(pc.locale || pc.lang) - try { return new URLSearchParams(location.search).get('lang') || document.documentElement.lang || 'en' } catch { return 'en' } -} - -/** - * @returns {Promise<{ map, provider } | { error: string, provider }>} resolves once the style has loaded - */ -export async function createBoardMap(el, profileConfig, { center = [60, 20], zoom = 1.4, whiteboard = false, boundaries = true, background = '#f3f3f1', token = '' } = {}) { - const provider = resolveMapProvider(profileConfig) - try { await ensureEngineScript(provider) } catch (_) { /* reported below */ } - const gl = engineGlobal(provider) - if (!gl) return { provider, error: provider === 'maplibre' ? 'MapLibre' : 'Mapbox' } - installRtlTextPlugin(gl) - const css = engineStylesheetUrl(provider) - if (css) await ensureEngineCSS(el, css) - const lang = readerLang(profileConfig) - // The host's token by the same ladder the production maps use: profile-config tk, then the token - // ProfileLoader provides (a tokenUrl token-service answer, or the page's shared token), then the page global. - const tk = profileConfig?.tk || token || (typeof window !== 'undefined' && window.MAP_TOKEN) || '' - const map = new gl.Map({ - container: el, - style: resolveNamedStyle('paper', provider).style || 'mapbox://styles/mapbox/light-v11', - accessToken: tk, - locale: engineUiLocale(lang, provider), - center, zoom, projection: 'mercator', attributionControl: false, - // a whiteboard is not a world: no wrap-around copies of the tapestry at low zoom - renderWorldCopies: !whiteboard, - }) - addMapAttribution(map, { boundaries, basemap: !whiteboard }) - await new Promise((res) => map.once('load', res)) - if (whiteboard) { - for (const l of map.getStyle().layers || []) { - try { - if (l.type === 'background') map.setPaintProperty(l.id, 'background-color', background) - else map.setLayoutProperty(l.id, 'visibility', 'none') - } catch (_) { /* a layer the engine will not touch is left as it is */ } - } - } else { - try { localizeBasemapLabels(map, lang) } catch (_) { /* a nicety, never a broken map */ } - } - return { map, provider } -} - -/** Reduced motion, read LIVE (never cached): under it every camera change is a jump. */ -export function reducedMotion() { - try { return window.matchMedia('(prefers-reduced-motion: reduce)').matches } catch (_) { return false } -} diff --git a/plugin-designs/gospel-internet/src/components/GiBoundedSetMap.vue b/plugin-designs/gospel-internet/src/components/GiBoundedSetMap.vue index 8b2d75b8d..72af5e0ba 100644 --- a/plugin-designs/gospel-internet/src/components/GiBoundedSetMap.vue +++ b/plugin-designs/gospel-internet/src/components/GiBoundedSetMap.vue @@ -22,7 +22,7 @@ import { useShadowStyles } from '@map/composables/useShadowStyles.js' import { labelFont } from '@map/maps/label-font.js' import { whiteboardProjection } from '@map/maps/whiteboard-projection.js' import { addTieredPointLayers } from '@map/charts/whiteboard/tiered-points.js' -import { createBoardMap, reducedMotion } from '../boardEngine.js' +import { createWhiteboardMap, reducedMotion } from '@map/maps/whiteboard-map.js' import { useBoardState } from '../useBoard.js' import { useGiT } from '../useGiT.js' import { layoutBoundedSet } from '@map/components/charts/boundedSet.js' @@ -83,11 +83,11 @@ function openAndFrame(slug, feature) { onMounted(async () => { try { - const made = await createBoardMap(el.value, board.profileConfig?.value || {}, + const made = await createWhiteboardMap(el.value, board.profileConfig?.value || {}, { whiteboard: true, boundaries: false, center: [0, 0], zoom: 1 }) const map = markRaw(made.map || made) mapRef.value = map - // ★ createBoardMap ALREADY AWAITS LOAD. boardEngine resolves only after + // ★ createWhiteboardMap ALREADY AWAITS LOAD. The library helper resolves only after // `await new Promise(res => map.once('load', res))`, so by the time this runs the event has // fired and will not fire again — a second `map.on('load', …)` never runs and the layers are // never added. The chart then draws a basemap-less whiteboard with no source on it: empty, @@ -141,7 +141,7 @@ onMounted(async () => { } catch (_) { /* no ResizeObserver: the initial fit stands */ } } // ★ CALLED UNCONDITIONALLY, AND THE GUARD THAT USED TO BE HERE IS WHY THIS WAS BROKEN TWICE. - // createBoardMap only resolves after `await map.once('load')`, so by here the event HAS fired. + // createWhiteboardMap only resolves after `await map.once('load')`, so by here the event HAS fired. // The previous line read `map.loaded() ? setup() : map.once('load', setup)` — but loaded() // does not mean "the load event fired", it means "the style AND every tile are loaded", and // the basemap tiles were still arriving. So it returned false, took the else branch, and diff --git a/plugin-designs/gospel-internet/src/components/GiCenteredSetMap.vue b/plugin-designs/gospel-internet/src/components/GiCenteredSetMap.vue index 45aad8372..813fa0faa 100644 --- a/plugin-designs/gospel-internet/src/components/GiCenteredSetMap.vue +++ b/plugin-designs/gospel-internet/src/components/GiCenteredSetMap.vue @@ -23,7 +23,7 @@ import { useShadowStyles } from '@map/composables/useShadowStyles.js' import { labelFont } from '@map/maps/label-font.js' import { whiteboardProjection } from '@map/maps/whiteboard-projection.js' import { addTieredPointLayers } from '@map/charts/whiteboard/tiered-points.js' -import { createBoardMap, reducedMotion } from '../boardEngine.js' +import { createWhiteboardMap, reducedMotion } from '@map/maps/whiteboard-map.js' import { useBoardState } from '../useBoard.js' import { useGiT } from '../useGiT.js' import { ringInfo, CENTRE_COLOR } from '../ringInfo.js' @@ -147,11 +147,11 @@ function openAndFrame(slug, feature) { onMounted(async () => { try { - const made = await createBoardMap(el.value, board.profileConfig?.value || {}, + const made = await createWhiteboardMap(el.value, board.profileConfig?.value || {}, { whiteboard: true, boundaries: false, center: [0, 0], zoom: 1 }) const map = markRaw(made.map || made) mapRef.value = map - // ★ createBoardMap ALREADY AWAITS LOAD. boardEngine resolves only after + // ★ createWhiteboardMap ALREADY AWAITS LOAD. The library helper resolves only after // `await new Promise(res => map.once('load', res))`, so by the time this runs the event has // fired and will not fire again — a second `map.on('load', …)` never runs and the layers are // never added. The chart then draws a basemap-less whiteboard with no source on it: empty, @@ -209,7 +209,7 @@ onMounted(async () => { ready.value = true } // ★ CALLED UNCONDITIONALLY, AND THE GUARD THAT USED TO BE HERE IS WHY THIS WAS BROKEN TWICE. - // createBoardMap only resolves after `await map.once('load')`, so by here the event HAS fired. + // createWhiteboardMap only resolves after `await map.once('load')`, so by here the event HAS fired. // The previous line read `map.loaded() ? setup() : map.once('load', setup)` — but loaded() // does not mean "the load event fired", it means "the style AND every tile are loaded", and // the basemap tiles were still arriving. So it returned false, took the else branch, and diff --git a/plugin-designs/gospel-internet/src/components/GiMap.vue b/plugin-designs/gospel-internet/src/components/GiMap.vue index df055ab25..7f3175b33 100644 --- a/plugin-designs/gospel-internet/src/components/GiMap.vue +++ b/plugin-designs/gospel-internet/src/components/GiMap.vue @@ -37,7 +37,7 @@ import ShareButton from '@map/components/map-controls/ShareButton.vue' import HelpButton from '@map/components/map-controls/HelpButton.vue' import { useBoardState, EMBED_PATH } from '../useBoard.js' import { useGiT } from '../useGiT.js' -import { createBoardMap, reducedMotion } from '../boardEngine.js' +import { createWhiteboardMap, reducedMotion } from '@map/maps/whiteboard-map.js' import { ACCESS_DETAILS, BANDS, DETAIL_FILL, DETAIL_RIM, BAND_COLOR, label } from '../model.js' const T = useGiT() @@ -285,7 +285,16 @@ onMounted(async () => { }).catch(() => { adm1Table = null }) try { if (!mapEl.value) return - const made = await createBoardMap(mapEl.value, board.profileConfig.value, { token: injectedToken?.value || '' }) + // ★ THE FOUR OPTIONS ARE STATED, NEVER DEFAULTED. This is the ONE geographic caller — the three + // whiteboard callers all pass whiteboard:true — and the library's defaults are the inverse of what + // a real map needs (whiteboard:true, boundaries:false, centre [0,0], zoom 1), because it was + // extracted for the whiteboard case. Leaning on defaults here meant a blank basemap and no + // boundary attribution the moment the import moved. Stating them makes this call read the same + // against either module. + const made = await createWhiteboardMap(mapEl.value, board.profileConfig.value, { + whiteboard: false, boundaries: true, center: [60, 20], zoom: 1.4, + token: injectedToken?.value || '', + }) if (made.error) { err.value = T('messages.engineMissing', `The ${made.error} map engine did not load.`, { engine: made.error }); return } const map = made.map if (unmounted) { try { map.remove() } catch (_) {} return } diff --git a/plugin-designs/gospel-internet/src/components/GiTree.vue b/plugin-designs/gospel-internet/src/components/GiTree.vue index fd8cde938..617bb1f0f 100644 --- a/plugin-designs/gospel-internet/src/components/GiTree.vue +++ b/plugin-designs/gospel-internet/src/components/GiTree.vue @@ -35,7 +35,7 @@ import ZoomOutButton from '@map/components/map-controls/ZoomOutButton.vue' import HelpButton from '@map/components/map-controls/HelpButton.vue' import { useBoardState } from '../useBoard.js' import { useGiT } from '../useGiT.js' -import { createBoardMap, reducedMotion } from '../boardEngine.js' +import { createWhiteboardMap, reducedMotion } from '@map/maps/whiteboard-map.js' import { treeScope, TREE_COLOR } from '../model.js' import { buildTreePoster, posterPrintHtml } from '../treePoster.js' import { openPrintFrame } from '@map/composables/printInPlace.js' @@ -225,7 +225,7 @@ function frame(coords) { async function build() { const t = await board.loadTree() if (unmounted || !el.value || mapRef.value) return - const made = await createBoardMap(el.value, board.profileConfig.value, { whiteboard: true, boundaries: false, center: [0, 0], zoom: 1, token: injectedToken?.value || '' }) + const made = await createWhiteboardMap(el.value, board.profileConfig.value, { whiteboard: true, boundaries: false, center: [0, 0], zoom: 1, token: injectedToken?.value || '' }) if (made.error) { err.value = T('messages.engineMissing', `The ${made.error} map engine did not load.`, { engine: made.error }); return } if (unmounted) { try { made.map.remove() } catch (_) {} return } const map = made.map