Merge main into bundler-2/work

This commit is contained in:
Doxa Maps 2026-09-26 01:33:36 +00:00
commit 003fd1c92b
14 changed files with 66 additions and 52 deletions

View file

@ -47,7 +47,7 @@ plugin-designs/<your-map>/ ← one FOLDER = one BUNDLE, named after the fold
├── overview.vue ← one .vue = one PROFILE (flat in the folder — no profiles/ subfolder)
└── detail.vue ← another profile in the SAME bundle
builds to ─────────────► public/doxa-maps/<your-map>/<your-map>.js (one file)
builds to ─────────────► public/dx-maps/<your-map>/<your-map>.js (one file)
```
- **Folder name = bundle name.** Folders starting with `_` or `.` are skipped (handy for WIP).
@ -55,9 +55,9 @@ plugin-designs/<your-map>/ ← one FOLDER = one BUNDLE, named after the fold
- **Pick the profile at embed time** with the `profile` field — every profile shares one bundle:
```html
<script src="/doxa-maps/<your-map>/<your-map>.js"></script>
<doxa-map profile-config='{"profile":"overview","tk":"pk.eyJ..."}'></doxa-map>
<doxa-map profile-config='{"profile":"detail","tk":"pk.eyJ..."}'></doxa-map>
<script src="/dx-maps/<your-map>/<your-map>.js"></script>
<your-map profile-config='{"profile":"overview","tk":"pk.eyJ..."}'></your-map>
<your-map profile-config='{"profile":"detail","tk":"pk.eyJ..."}'></your-map>
```
## Add a map — three steps
@ -69,7 +69,7 @@ mkdir -p plugin-designs/<your-map>
# <name>.vue ← your screen, built from @map/* blocks (flat in the folder)
# 2. build
npm run build # → public/doxa-maps/<your-map>/<your-map>.js
npm run build # → public/dx-maps/<your-map>/<your-map>.js
# (just this one: BUNDLE=<your-map> npm run build:bundle)
# 3. embed the script tag + element (dev staging at `npm run dev` shows a paste-ready snippet)

View file

@ -50,7 +50,9 @@ const urlLang = (() => {
try { const q = new URLSearchParams(window.location.search); return q.get('lang') || q.get('locale') || '' } catch { return '' }
})()
const locale = computed(() => {
const raw = String(config.value.locale || urlLang || (typeof document !== 'undefined' && document.documentElement.lang) || 'en')
// profile-config `locale` OR `lang`: composites forward the page language as `lang`, so a card
// nested in one missed it and fell back to English (documentation/reference-universal-props.md).
const raw = String(config.value.locale || config.value.lang || urlLang || (typeof document !== 'undefined' && document.documentElement.lang) || 'en')
const base = raw.split('-')[0].toLowerCase()
return STRINGS[raw] ? raw : (STRINGS[base] ? base : 'en')
})

View file

@ -86,6 +86,18 @@ export function whenGeojsonDrawn(map, timeoutMs = 45000) {
})
}
// Switch projection on EITHER engine. The argument shape is engine-specific: Mapbox takes the name
// ('globe'), MapLibre 5 takes a specification object ({ type: 'globe' }). The views passed the bare
// string, so under provider=maplibre the Globe/Mercator toggle did nothing (or threw inside a
// style.load handler). Guarded: a style that cannot take the projection must not break the view.
export function setProjectionFor(map, name) {
if (!map || !name) return
try {
const ml = typeof window !== 'undefined' ? window.maplibregl : null
map.setProjection(ml && ml.Map && map instanceof ml.Map ? { type: name } : name)
} catch (_) { /* the style does not support it: keep the current projection */ }
}
// Verification handle (the gospel-resources __grState contract): what the ACTIVE view's map
// actually did, so a check can assert "loaded" rather than inferring it from missing requests.
// window.__pplrMap() -> { engine, loaded, styleLayers, pplrLayers }

View file

@ -1,5 +1,5 @@
<script setup>
import { dataUrl, glFor, exposeMap, whenGeojsonDrawn } from '../utils/pplrData.js'
import { dataUrl, glFor, exposeMap, whenGeojsonDrawn, setProjectionFor } from '../utils/pplrData.js'
/**
* pplr-language-families.vue — PG pins colored by PPLR ethnolinguistic family.
*
@ -361,7 +361,7 @@ const detailColor = computed(() => {
// ── Projection / basemap ──────────────────────────────────────────────────────
function toggleProjection() {
projection.value = projection.value === 'globe' ? 'mercator' : 'globe'
if (map.value) map.value.setProjection(projection.value)
if (map.value) setProjectionFor(map.value, projection.value)
}
function setBasemap(key) {
@ -371,7 +371,7 @@ function setBasemap(key) {
showBasemap.value = false
const style = BASEMAPS.find(b => b.key === key)?.url
if (!style || !map.value) return
map.value.once('style.load', () => { setupLayers(); map.value.setProjection(projection.value) })
map.value.once('style.load', () => { setupLayers(); setProjectionFor(map.value, projection.value) })
// diff:false suppresses Mapbox's "Unimplemented: setSprite" warning between
// dark-v11 and light-v11. We re-add layers in setupLayers anyway.
map.value.setStyle(resolveNamedStyle(style, mapProvider).style, { diff: false })

View file

@ -1,5 +1,5 @@
<script setup>
import { dataUrl, glFor, exposeMap, whenGeojsonDrawn } from '../utils/pplrData.js'
import { dataUrl, glFor, exposeMap, whenGeojsonDrawn, setProjectionFor } from '../utils/pplrData.js'
/**
* pplr-religion.vue — PG pins colored by primary religion.
*
@ -44,7 +44,7 @@ function reload() { window.location.reload() }
function toggleProjection() {
projection.value = projection.value === 'globe' ? 'mercator' : 'globe'
if (map.value) map.value.setProjection(projection.value)
if (map.value) setProjectionFor(map.value, projection.value)
}
function toggleTheme() {
@ -58,7 +58,7 @@ function setBasemap(key) {
showBasemap.value = false
const style = BASEMAPS.find(b => b.key === key)?.url
if (!style || !map.value) return
map.value.once('style.load', () => { setupLayers(); map.value.setProjection(projection.value) })
map.value.once('style.load', () => { setupLayers(); setProjectionFor(map.value, projection.value) })
// diff:false suppresses Mapbox's "Unimplemented: setSprite" warning between
// dark-v11 and light-v11. We re-add layers in setupLayers anyway.
map.value.setStyle(resolveNamedStyle(style, mapProvider).style, { diff: false })

View file

@ -1,5 +1,5 @@
<script setup>
import { dataUrl, glFor, exposeMap } from '../utils/pplrData.js'
import { dataUrl, glFor, exposeMap, setProjectionFor } from '../utils/pplrData.js'
/**
* pplr-rop-tree.vue — ROP hierarchy: Affinity Bloc → Cluster → People Group → PGIC.
*
@ -298,7 +298,7 @@ function flyToNode(node) {
// ── Projection / basemap ──────────────────────────────────────────────────────
function toggleProjection() {
projection.value = projection.value === 'globe' ? 'mercator' : 'globe'
if (map.value) map.value.setProjection(projection.value)
if (map.value) setProjectionFor(map.value, projection.value)
}
function setBasemap(key) {
@ -308,7 +308,7 @@ function setBasemap(key) {
showBasemap.value = false
const style = BASEMAPS.find(b => b.key === key)?.url
if (!style || !map.value) return
map.value.once('style.load', () => { setupLayers(); map.value.setProjection(projection.value) })
map.value.once('style.load', () => { setupLayers(); setProjectionFor(map.value, projection.value) })
// diff:false skips Mapbox's "try-to-diff-the-style" path which logs an
// "Unimplemented: setSprite" warning whenever sprite sheets differ between
// dark-v11 and light-v11. We re-add all our layers in setupLayers anyway,

View file

@ -493,7 +493,7 @@
"bundle": "doxa-vision-card",
"scriptUrl": "./doxa-vision-card/doxa-vision-card.js",
"output": "dx-maps/doxa-vision-card/doxa-vision-card.js",
"integrity": "sha384-ga7XpXFyxJDd8VrtMejKuLRx8YFuxJoT+DXkcpehJXptsM4phiKamyTBYJpudSSj",
"integrity": "sha384-ZChtIPhUBbNtPSWFmaGfEJWMn/LnEJWC+/qyPGAOYSRenGdrx+ZjoOchfsLg0Epr",
"elements": [
"doxa-vision-card"
],
@ -509,7 +509,7 @@
"scriptUrl": "./doxa-vision-card/doxa-vision-card.js",
"embed": {
"iframe": "<iframe src=\"./doxa-vision-card/doxa-vision-card/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-vision-card/doxa-vision-card.js\" integrity=\"sha384-ga7XpXFyxJDd8VrtMejKuLRx8YFuxJoT+DXkcpehJXptsM4phiKamyTBYJpudSSj\" crossorigin=\"anonymous\"></script>\n<doxa-vision-card profile-config='{\"profile\":\"doxa-vision-card\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-vision-card>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-vision-card/doxa-vision-card.js\" integrity=\"sha384-ZChtIPhUBbNtPSWFmaGfEJWMn/LnEJWC+/qyPGAOYSRenGdrx+ZjoOchfsLg0Epr\" crossorigin=\"anonymous\"></script>\n<doxa-vision-card profile-config='{\"profile\":\"doxa-vision-card\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-vision-card>"
},
"instances": [
{
@ -837,7 +837,7 @@
"bundle": "pplr-data-maps",
"scriptUrl": "./pplr-data-maps/pplr-data-maps.js",
"output": "dx-maps/pplr-data-maps/pplr-data-maps.js",
"integrity": "sha384-e6wVt+dC86mFb8TgX2jBn7wh/R9gA6rGxNgS6umNBeNrir3TstupaJKf4XElRP2T",
"integrity": "sha384-pv5rEQVt1LSyoZCiLV2+/RLokA7N+KfhfnAU5LvCISKkBIMvWsTJh1HksTlcSyQY",
"elements": [
"pplr-data-maps"
],
@ -853,7 +853,7 @@
"scriptUrl": "./pplr-data-maps/pplr-data-maps.js",
"embed": {
"iframe": "<iframe src=\"./pplr-data-maps/pplr-data-maps/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./pplr-data-maps/pplr-data-maps.js\" integrity=\"sha384-e6wVt+dC86mFb8TgX2jBn7wh/R9gA6rGxNgS6umNBeNrir3TstupaJKf4XElRP2T\" crossorigin=\"anonymous\"></script>\n<pplr-data-maps profile-config='{\"profile\":\"pplr-data-maps\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></pplr-data-maps>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./pplr-data-maps/pplr-data-maps.js\" integrity=\"sha384-pv5rEQVt1LSyoZCiLV2+/RLokA7N+KfhfnAU5LvCISKkBIMvWsTJh1HksTlcSyQY\" crossorigin=\"anonymous\"></script>\n<pplr-data-maps profile-config='{\"profile\":\"pplr-data-maps\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></pplr-data-maps>"
},
"bundle": "pplr-data-maps"
}

File diff suppressed because one or more lines are too long

View file

@ -26,7 +26,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <doxa-vision-card> custom element. -->
<script src="../../doxa-vision-card.js" integrity="sha384-ga7XpXFyxJDd8VrtMejKuLRx8YFuxJoT+DXkcpehJXptsM4phiKamyTBYJpudSSj" crossorigin="anonymous"></script>
<script src="../../doxa-vision-card.js" integrity="sha384-ZChtIPhUBbNtPSWFmaGfEJWMn/LnEJWC+/qyPGAOYSRenGdrx+ZjoOchfsLg0Epr" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -26,7 +26,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <doxa-vision-card> custom element. -->
<script src="../../doxa-vision-card.js" integrity="sha384-ga7XpXFyxJDd8VrtMejKuLRx8YFuxJoT+DXkcpehJXptsM4phiKamyTBYJpudSSj" crossorigin="anonymous"></script>
<script src="../../doxa-vision-card.js" integrity="sha384-ZChtIPhUBbNtPSWFmaGfEJWMn/LnEJWC+/qyPGAOYSRenGdrx+ZjoOchfsLg0Epr" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -26,7 +26,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <doxa-vision-card> custom element. -->
<script src="../doxa-vision-card.js" integrity="sha384-ga7XpXFyxJDd8VrtMejKuLRx8YFuxJoT+DXkcpehJXptsM4phiKamyTBYJpudSSj" crossorigin="anonymous"></script>
<script src="../doxa-vision-card.js" integrity="sha384-ZChtIPhUBbNtPSWFmaGfEJWMn/LnEJWC+/qyPGAOYSRenGdrx+ZjoOchfsLg0Epr" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -493,7 +493,7 @@
"bundle": "doxa-vision-card",
"scriptUrl": "./doxa-vision-card/doxa-vision-card.js",
"output": "dx-maps/doxa-vision-card/doxa-vision-card.js",
"integrity": "sha384-ga7XpXFyxJDd8VrtMejKuLRx8YFuxJoT+DXkcpehJXptsM4phiKamyTBYJpudSSj",
"integrity": "sha384-ZChtIPhUBbNtPSWFmaGfEJWMn/LnEJWC+/qyPGAOYSRenGdrx+ZjoOchfsLg0Epr",
"elements": [
"doxa-vision-card"
],
@ -509,7 +509,7 @@
"scriptUrl": "./doxa-vision-card/doxa-vision-card.js",
"embed": {
"iframe": "<iframe src=\"./doxa-vision-card/doxa-vision-card/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-vision-card/doxa-vision-card.js\" integrity=\"sha384-ga7XpXFyxJDd8VrtMejKuLRx8YFuxJoT+DXkcpehJXptsM4phiKamyTBYJpudSSj\" crossorigin=\"anonymous\"></script>\n<doxa-vision-card profile-config='{\"profile\":\"doxa-vision-card\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-vision-card>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-vision-card/doxa-vision-card.js\" integrity=\"sha384-ZChtIPhUBbNtPSWFmaGfEJWMn/LnEJWC+/qyPGAOYSRenGdrx+ZjoOchfsLg0Epr\" crossorigin=\"anonymous\"></script>\n<doxa-vision-card profile-config='{\"profile\":\"doxa-vision-card\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-vision-card>"
},
"instances": [
{
@ -837,7 +837,7 @@
"bundle": "pplr-data-maps",
"scriptUrl": "./pplr-data-maps/pplr-data-maps.js",
"output": "dx-maps/pplr-data-maps/pplr-data-maps.js",
"integrity": "sha384-e6wVt+dC86mFb8TgX2jBn7wh/R9gA6rGxNgS6umNBeNrir3TstupaJKf4XElRP2T",
"integrity": "sha384-pv5rEQVt1LSyoZCiLV2+/RLokA7N+KfhfnAU5LvCISKkBIMvWsTJh1HksTlcSyQY",
"elements": [
"pplr-data-maps"
],
@ -853,7 +853,7 @@
"scriptUrl": "./pplr-data-maps/pplr-data-maps.js",
"embed": {
"iframe": "<iframe src=\"./pplr-data-maps/pplr-data-maps/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./pplr-data-maps/pplr-data-maps.js\" integrity=\"sha384-e6wVt+dC86mFb8TgX2jBn7wh/R9gA6rGxNgS6umNBeNrir3TstupaJKf4XElRP2T\" crossorigin=\"anonymous\"></script>\n<pplr-data-maps profile-config='{\"profile\":\"pplr-data-maps\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></pplr-data-maps>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./pplr-data-maps/pplr-data-maps.js\" integrity=\"sha384-pv5rEQVt1LSyoZCiLV2+/RLokA7N+KfhfnAU5LvCISKkBIMvWsTJh1HksTlcSyQY\" crossorigin=\"anonymous\"></script>\n<pplr-data-maps profile-config='{\"profile\":\"pplr-data-maps\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></pplr-data-maps>"
},
"bundle": "pplr-data-maps"
}

File diff suppressed because one or more lines are too long

View file

@ -30,7 +30,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <pplr-data-maps> custom element. -->
<script src="../pplr-data-maps.js" integrity="sha384-e6wVt+dC86mFb8TgX2jBn7wh/R9gA6rGxNgS6umNBeNrir3TstupaJKf4XElRP2T" crossorigin="anonymous"></script>
<script src="../pplr-data-maps.js" integrity="sha384-pv5rEQVt1LSyoZCiLV2+/RLokA7N+KfhfnAU5LvCISKkBIMvWsTJh1HksTlcSyQY" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order: