Take main

# Conflicts:
#	public/doxa-maps/index.html
This commit is contained in:
doxa-maps-88655c70 2026-09-25 04:50:21 +00:00
commit c2e8d9afed
18 changed files with 213 additions and 82 deletions

View file

@ -39,7 +39,12 @@ const page = await (await browser.newContext({ viewport: { width: 1280, height:
let current = '(page load)'
const seen = {}
const note = (kind, v) => { const s = (seen[current] = seen[current] || { errors: [], failed: [] }); s[kind].push(v) }
page.on('pageerror', (e) => note('errors', String(e.message || e).slice(0, 160)))
// Keep the top of the STACK, not just the message: a message like "reading 'getRootNode'" names no
// caller, and the owner cannot fix it without knowing which call site threw (asked 2026-09-25).
page.on('pageerror', (e) => {
const frames = String(e.stack || '').split('\n').slice(1, 5).map((l) => l.trim().replace(/https?:\/\/[^/]+/, '').replace(/\?v=[0-9a-f]+/, '')).join(' <- ')
note('errors', String(e.message || e).slice(0, 160) + (frames ? ' [' + frames + ']' : ''))
})
page.on('response', (r) => {
if (r.status() < 400) return
let host = ''

View file

@ -497,6 +497,36 @@ const VTL_SHADOW_CSS = `
.vtl.rail-collapsed .vtl-tier { padding-left:8px; }
.vtl.rail-collapsed .vtl-tl, .vtl.rail-collapsed .vtl-td, .vtl.rail-collapsed .vtl-cap, .vtl.rail-collapsed .vtl-chev { display:inline; }
}
/* ★ RIGHT-TO-LEFT (ar). THIS BLOCK EXISTS TWICE (VTL_SHADOW_CSS and the scoped style block); keep them
byte-identical. Everything above is written in PHYSICAL terms (left:0 rail, left:256px content,
translateX(-…) slides), so an Arabic shell kept the rail on the left with LTR geometry (reported by
studio for cell E, 2026-09-25). These are ADDITIVE overrides scoped by :dir(rtl), so an LTR consumer
matches none of them and renders exactly as before. :dir() follows the dir attribute through the
shadow boundary, so either a consumer setting dir="rtl" on the layout or an RTL host page flips it.
translateX has no logical form, which is why this is a mirror block rather than logical properties. */
.vtl:dir(rtl) .vtl-nav { left:auto; right:0; border-right:0; border-left:1px solid var(--vtl-border); }
.vtl:dir(rtl) .vtl-main { left:0; right:256px; }
.vtl:dir(rtl) .vtl-tab, .vtl:dir(rtl) .vtl-tier-btn { text-align:right; }
.vtl:dir(rtl) .vtl-meta, .vtl:dir(rtl) .vtl-td, .vtl:dir(rtl) .vtl-chev { margin-left:0; margin-right:auto; }
.vtl:dir(rtl) .vtl-chev:not(.open) { transform:scaleX(-1); }
.vtl:dir(rtl) .vtl-collapse-ic svg { transform:scaleX(-1); }
.vtl:dir(rtl) .vtl-fab { left:auto; right:calc(12px + env(safe-area-inset-right)); }
.vtl.rail-collapsed:dir(rtl) .vtl-nav { transform:translateX(184px); }
.vtl.rail-collapsed:dir(rtl) .vtl-main { left:0; right:72px; }
.vtl.rail-collapsed:dir(rtl) .vtl-collapse { padding-left:10px; padding-right:184px; }
.vtl.rail-collapsed:dir(rtl) .vtl-tab { padding-left:12px; padding-right:188px; }
.vtl.rail-collapsed:dir(rtl) .vtl-tier { padding-left:8px; padding-right:196px; }
@media (max-width: 760px) {
.vtl:dir(rtl) .vtl-nav { transform:translateX(100%); }
.vtl.nav-open:dir(rtl) .vtl-nav { transform:translateX(0); }
.vtl:dir(rtl) .vtl-main, .vtl.rail-collapsed:dir(rtl) .vtl-main { left:0; right:0; }
.vtl.rail-collapsed:dir(rtl) .vtl-nav { transform:translateX(100%); }
.vtl.rail-collapsed.nav-open:dir(rtl) .vtl-nav { transform:translateX(0); }
.vtl.rail-collapsed:dir(rtl) .vtl-collapse { padding-left:10px; padding-right:10px; }
.vtl.rail-collapsed:dir(rtl) .vtl-tab { padding-left:12px; padding-right:10px; }
.vtl.rail-collapsed:dir(rtl) .vtl-tier { padding-left:8px; padding-right:8px; }
}
`
export default defineComponent({
@ -955,4 +985,34 @@ export default defineComponent({
.vtl.rail-collapsed .vtl-tier { padding-left:8px; }
.vtl.rail-collapsed .vtl-tl, .vtl.rail-collapsed .vtl-td, .vtl.rail-collapsed .vtl-cap, .vtl.rail-collapsed .vtl-chev { display:inline; }
}
/* ★ RIGHT-TO-LEFT (ar). THIS BLOCK EXISTS TWICE (VTL_SHADOW_CSS and the scoped style block); keep them
byte-identical. Everything above is written in PHYSICAL terms (left:0 rail, left:256px content,
translateX(-…) slides), so an Arabic shell kept the rail on the left with LTR geometry (reported by
studio for cell E, 2026-09-25). These are ADDITIVE overrides scoped by :dir(rtl), so an LTR consumer
matches none of them and renders exactly as before. :dir() follows the dir attribute through the
shadow boundary, so either a consumer setting dir="rtl" on the layout or an RTL host page flips it.
translateX has no logical form, which is why this is a mirror block rather than logical properties. */
.vtl:dir(rtl) .vtl-nav { left:auto; right:0; border-right:0; border-left:1px solid var(--vtl-border); }
.vtl:dir(rtl) .vtl-main { left:0; right:256px; }
.vtl:dir(rtl) .vtl-tab, .vtl:dir(rtl) .vtl-tier-btn { text-align:right; }
.vtl:dir(rtl) .vtl-meta, .vtl:dir(rtl) .vtl-td, .vtl:dir(rtl) .vtl-chev { margin-left:0; margin-right:auto; }
.vtl:dir(rtl) .vtl-chev:not(.open) { transform:scaleX(-1); }
.vtl:dir(rtl) .vtl-collapse-ic svg { transform:scaleX(-1); }
.vtl:dir(rtl) .vtl-fab { left:auto; right:calc(12px + env(safe-area-inset-right)); }
.vtl.rail-collapsed:dir(rtl) .vtl-nav { transform:translateX(184px); }
.vtl.rail-collapsed:dir(rtl) .vtl-main { left:0; right:72px; }
.vtl.rail-collapsed:dir(rtl) .vtl-collapse { padding-left:10px; padding-right:184px; }
.vtl.rail-collapsed:dir(rtl) .vtl-tab { padding-left:12px; padding-right:188px; }
.vtl.rail-collapsed:dir(rtl) .vtl-tier { padding-left:8px; padding-right:196px; }
@media (max-width: 760px) {
.vtl:dir(rtl) .vtl-nav { transform:translateX(100%); }
.vtl.nav-open:dir(rtl) .vtl-nav { transform:translateX(0); }
.vtl:dir(rtl) .vtl-main, .vtl.rail-collapsed:dir(rtl) .vtl-main { left:0; right:0; }
.vtl.rail-collapsed:dir(rtl) .vtl-nav { transform:translateX(100%); }
.vtl.rail-collapsed.nav-open:dir(rtl) .vtl-nav { transform:translateX(0); }
.vtl.rail-collapsed:dir(rtl) .vtl-collapse { padding-left:10px; padding-right:10px; }
.vtl.rail-collapsed:dir(rtl) .vtl-tab { padding-left:12px; padding-right:10px; }
.vtl.rail-collapsed:dir(rtl) .vtl-tier { padding-left:8px; padding-right:8px; }
}
</style>

View file

@ -65,7 +65,7 @@ The gallery is tested to leave its host page as it found it: it does not scroll
your URL or `<title>`, or inherit your body typography (a blog's `font: 20px/3` stays outside the
box). It does add `@font-face` rules and the map engine's stylesheet to your `<head>`; fonts have to
be declared at document level to reach a shadow root, and the engine styles only target map classes.
Right-to-left pages keep their direction. `bun run check:gallery-embed` enforces all of this.
Direction follows the gallery's language: in Arabic the whole shell mirrors, with the tab rail on the right. `bun run check:gallery-embed` enforces all of this.
## What the server must send

View file

@ -57,6 +57,8 @@
the direct-element mount and the heavy-tab teardown machinery live in the default slot below,
driven by activeIndex. -->
<VerticalTabsLayout
:dir="uiDir"
:lang="uiLocale"
:title="heading"
:nav-label="t('navLabel')"
:header="showHeader"
@ -937,6 +939,11 @@ export default defineComponent({
},
// The locale the gallery's OWN words render in: the effective one when it ships, else English.
uiLocale() { return LOCALES.includes(this.effectiveLocale) ? this.effectiveLocale : 'en' },
// Direction FOLLOWS THE GALLERY'S LANGUAGE, set explicitly on the layout root (a fallthrough attr
// on VerticalTabsLayout's .vtl): Arabic lays the shell out right-to-left (the layout mirrors its
// rail under :dir(rtl)), every other shipped locale left-to-right — including an English gallery
// placed on some other RTL page, whose Latin rail must not be mirrored.
uiDir() { return this.uiLocale === 'ar' ? 'rtl' : 'ltr' },
// The engine a HOST asked for ('' = none; each map keeps its own ladder).
effectiveProvider() {
const p = String(this.provider || this.cfg.provider || '').trim().toLowerCase()
@ -1415,9 +1422,9 @@ export default defineComponent({
Measured 2026-09-25 on a host with `body{font:20px/3 Georgia; letter-spacing:2px}`: the gallery
root computed font-size 20px, line-height 60px, letter-spacing 2px — every rail row and map legend
re-flowed by the partner's blog styles. These are reset to the values the gallery is designed
against (its own leaf page's defaults). `direction` is deliberately NOT reset, so an RTL host page
still lays the gallery out RTL; `all: initial` would have reset it too. Family and colour are set by
the layout itself (.vtl). */
against (its own leaf page's defaults). `direction` is not reset here: the layout root carries an
explicit dir from the gallery's language (uiDir), which is what actually decides RTL. Family and
colour are set by the layout itself (.vtl). */
:host {
font-size: 16px; line-height: normal; font-weight: 400; font-style: normal; font-variant: normal;
font-stretch: normal; letter-spacing: normal; word-spacing: normal; text-transform: none;

View file

@ -370,12 +370,11 @@ const legendTitle = computed(() => 'Legend')
// wiring for desktop, mobile and embeds. STL also retitles itself "Progress".
const legendProgress = flatLegend.progress
provide('legendProgress', legendProgress)
// From the catalog (legend.colLabel.*), which every locale already translates. This was
// hard-coded English, the one column header left in English in the ar/zh/ru/hi embeds.
const legendColumnLabel = computed(() => {
const t = activeLegendType.value
if (t === 'prayer') return 'Prayer Status'
if (t === 'engagement') return 'Engagement Status'
if (t === 'adoption') return 'Adoption Status'
return 'Status'
const type = activeLegendType.value
return t('legend.colLabel.' + (['prayer', 'engagement', 'adoption'].includes(type) ? type : 'status'))
})
const _stlOpen = computed(() => uiStore.legendState !== 'collapsed')
function _onStlOpenChange(open) {

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-maps-spa> custom element. -->
<script src="../../doxa-maps-spa.js" integrity="sha384-Y0yYckt4MZMLTeUPpc1M6rFGH90giZOdN8VGaR+kKLX2OzBh8QFRzSphPKHRYr2V" crossorigin="anonymous"></script>
<script src="../../doxa-maps-spa.js" integrity="sha384-Gq3cr144mzszbujdJUx1pLYKNMRM9IHomwvdy0GO5B0CVO1LoJS5hsSKKFlcn0Am" 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-maps-spa> custom element. -->
<script src="../doxa-maps-spa.js" integrity="sha384-Y0yYckt4MZMLTeUPpc1M6rFGH90giZOdN8VGaR+kKLX2OzBh8QFRzSphPKHRYr2V" crossorigin="anonymous"></script>
<script src="../doxa-maps-spa.js" integrity="sha384-Gq3cr144mzszbujdJUx1pLYKNMRM9IHomwvdy0GO5B0CVO1LoJS5hsSKKFlcn0Am" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <doxa-map> custom element. -->
<script src="../../doxa-simple-map.js" integrity="sha384-G/NuJx27ipfilwAaJ9N9jsGgKtVlSMwkS9E/dv/NE5drDd56Z5KYewcu0hp3H2N2" crossorigin="anonymous"></script>
<script src="../../doxa-simple-map.js" integrity="sha384-9zRii7R8qYNcQie5wWdj4FOyXxc4f05gn8QvF6ynAs6OakpTBmEXxblDPyd4EUaD" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <doxa-map> custom element. -->
<script src="../../doxa-simple-map.js" integrity="sha384-G/NuJx27ipfilwAaJ9N9jsGgKtVlSMwkS9E/dv/NE5drDd56Z5KYewcu0hp3H2N2" crossorigin="anonymous"></script>
<script src="../../doxa-simple-map.js" integrity="sha384-9zRii7R8qYNcQie5wWdj4FOyXxc4f05gn8QvF6ynAs6OakpTBmEXxblDPyd4EUaD" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <doxa-map> custom element. -->
<script src="../doxa-simple-map.js" integrity="sha384-G/NuJx27ipfilwAaJ9N9jsGgKtVlSMwkS9E/dv/NE5drDd56Z5KYewcu0hp3H2N2" crossorigin="anonymous"></script>
<script src="../doxa-simple-map.js" integrity="sha384-9zRii7R8qYNcQie5wWdj4FOyXxc4f05gn8QvF6ynAs6OakpTBmEXxblDPyd4EUaD" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <doxa-map> custom element. -->
<script src="../../doxa-simple-map.js" integrity="sha384-G/NuJx27ipfilwAaJ9N9jsGgKtVlSMwkS9E/dv/NE5drDd56Z5KYewcu0hp3H2N2" crossorigin="anonymous"></script>
<script src="../../doxa-simple-map.js" integrity="sha384-9zRii7R8qYNcQie5wWdj4FOyXxc4f05gn8QvF6ynAs6OakpTBmEXxblDPyd4EUaD" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

View file

@ -302,7 +302,7 @@
"bundle": "doxa-maps-spa",
"scriptUrl": "./doxa-maps-spa/doxa-maps-spa.js",
"output": "doxa-maps/doxa-maps-spa/doxa-maps-spa.js",
"integrity": "sha384-Y0yYckt4MZMLTeUPpc1M6rFGH90giZOdN8VGaR+kKLX2OzBh8QFRzSphPKHRYr2V",
"integrity": "sha384-Gq3cr144mzszbujdJUx1pLYKNMRM9IHomwvdy0GO5B0CVO1LoJS5hsSKKFlcn0Am",
"elements": [
"doxa-maps-spa"
],
@ -327,7 +327,7 @@
"scriptUrl": "./doxa-maps-spa/doxa-maps-spa.js",
"embed": {
"iframe": "<iframe src=\"./doxa-maps-spa/doxa-maps-spa/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-maps-spa/doxa-maps-spa.js\" integrity=\"sha384-Y0yYckt4MZMLTeUPpc1M6rFGH90giZOdN8VGaR+kKLX2OzBh8QFRzSphPKHRYr2V\" crossorigin=\"anonymous\"></script>\n<doxa-maps-spa profile-config='{\"profile\":\"doxa-maps-spa\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-maps-spa>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-maps-spa/doxa-maps-spa.js\" integrity=\"sha384-Gq3cr144mzszbujdJUx1pLYKNMRM9IHomwvdy0GO5B0CVO1LoJS5hsSKKFlcn0Am\" crossorigin=\"anonymous\"></script>\n<doxa-maps-spa profile-config='{\"profile\":\"doxa-maps-spa\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-maps-spa>"
},
"instances": [
{
@ -396,7 +396,7 @@
"bundle": "doxa-simple-map",
"scriptUrl": "./doxa-simple-map/doxa-simple-map.js",
"output": "doxa-maps/doxa-simple-map/doxa-simple-map.js",
"integrity": "sha384-G/NuJx27ipfilwAaJ9N9jsGgKtVlSMwkS9E/dv/NE5drDd56Z5KYewcu0hp3H2N2",
"integrity": "sha384-9zRii7R8qYNcQie5wWdj4FOyXxc4f05gn8QvF6ynAs6OakpTBmEXxblDPyd4EUaD",
"elements": [
"doxa-map",
"doxa-simple-map"
@ -413,7 +413,7 @@
"scriptUrl": "./doxa-simple-map/doxa-simple-map.js",
"embed": {
"iframe": "<iframe src=\"./doxa-simple-map/doxa-simple-map/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-simple-map/doxa-simple-map.js\" integrity=\"sha384-G/NuJx27ipfilwAaJ9N9jsGgKtVlSMwkS9E/dv/NE5drDd56Z5KYewcu0hp3H2N2\" crossorigin=\"anonymous\"></script>\n<doxa-map profile-config='{\"profile\":\"doxa-simple-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-9zRii7R8qYNcQie5wWdj4FOyXxc4f05gn8QvF6ynAs6OakpTBmEXxblDPyd4EUaD\" crossorigin=\"anonymous\"></script>\n<doxa-map profile-config='{\"profile\":\"doxa-simple-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-map>"
},
"instances": [
{
@ -799,7 +799,7 @@
"bundle": "plugin-studio",
"scriptUrl": "./plugin-studio/plugin-studio.js",
"output": "doxa-maps/plugin-studio/plugin-studio.js",
"integrity": "sha384-NXCBL46038pXHJb8lEhrC/U2ZIBLvsevalJdp9/OU/8cp3LGx2TinkGoY6j8usV3",
"integrity": "sha384-ueMdhH+oGiRQsFSZcddK14WPENJ18oi4hSEwzK8pZNNvHCRoVyXyJGzgsJr0sMPG",
"elements": [
"plugin-studio"
],
@ -827,7 +827,7 @@
"scriptUrl": "./plugin-studio/plugin-studio.js",
"embed": {
"iframe": "<iframe src=\"./plugin-studio/plugin-studio/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=\"./plugin-studio/plugin-studio.js\" integrity=\"sha384-NXCBL46038pXHJb8lEhrC/U2ZIBLvsevalJdp9/OU/8cp3LGx2TinkGoY6j8usV3\" crossorigin=\"anonymous\"></script>\n<plugin-studio profile-config='{\"profile\":\"plugin-studio\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></plugin-studio>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./plugin-studio/plugin-studio.js\" integrity=\"sha384-ueMdhH+oGiRQsFSZcddK14WPENJ18oi4hSEwzK8pZNNvHCRoVyXyJGzgsJr0sMPG\" crossorigin=\"anonymous\"></script>\n<plugin-studio profile-config='{\"profile\":\"plugin-studio\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></plugin-studio>"
},
"instances": [
{

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 <plugin-studio> custom element. -->
<script src="../plugin-studio.js" integrity="sha384-NXCBL46038pXHJb8lEhrC/U2ZIBLvsevalJdp9/OU/8cp3LGx2TinkGoY6j8usV3" crossorigin="anonymous"></script>
<script src="../plugin-studio.js" integrity="sha384-ueMdhH+oGiRQsFSZcddK14WPENJ18oi4hSEwzK8pZNNvHCRoVyXyJGzgsJr0sMPG" 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 <plugin-studio> custom element. -->
<script src="../../plugin-studio.js" integrity="sha384-NXCBL46038pXHJb8lEhrC/U2ZIBLvsevalJdp9/OU/8cp3LGx2TinkGoY6j8usV3" crossorigin="anonymous"></script>
<script src="../../plugin-studio.js" integrity="sha384-ueMdhH+oGiRQsFSZcddK14WPENJ18oi4hSEwzK8pZNNvHCRoVyXyJGzgsJr0sMPG" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order: