Resolve a composite's child bundles against the tree root, not a guessed depth
Some checks are pending
build-gate / build (push) Waiting to run

Driver-reported console text:
  Could not load <doxa-research-map> from ../../doxa-research-map/doxa-research-map.js

A composite declares children as src: '../../<bundle>/<bundle>.js'. That literal
is right from a one-deep leaf and wrong from a two-deep one, and the generator
emits both:
  .../research-map-plus-grid/index.html          ../../ -> /doxa-maps/          200
  .../research-map-plus-grid/default/index.html  ../../ -> /doxa-maps/rmpg/     404

So the path encoded an assumption about the host's depth that the host does not
control and cannot see. It survived because every gate exercised the shallow
leaf, which is genuinely fine.

StackLayout now derives the tree root from the document's own pathname (up to
and including the /doxa-maps/ segment) and rebuilds the path from there --
depth-independent by construction, so a third leaf depth cannot reintroduce it.
Absolute, protocol-relative and root-relative srcs pass through untouched, and
a missing /doxa-maps/ segment returns the original string, so this can only fix
a path, never break a working one.

Measured, both leaves, real browser with live data:
  one-deep  mapEl=true mapH=792 canvasH=856 0 4xx
  two-deep  mapEl=true mapH=792 canvasH=856 0 4xx   (was 404, map absent)

Also carries source that had been left behind by its own built output:
the cities/gospel legend bounding (shipped in 5008a6f as output only) and the
per-profile config carrier in generate-tree.mjs, plus a new fits-screen clause
that wheels inside every column rather than one.
This commit is contained in:
doxa-maps-88655c70 2026-09-23 19:34:28 +00:00
parent d97cf9eab5
commit 1cf4e89dab
16 changed files with 218 additions and 54 deletions

View file

@ -442,6 +442,61 @@ async function runComposite() {
if (zoomChangedBelow) fail('c', `wheel BELOW the map changed zoom (${zoomBefore2} -> ${zoomAfter2}) — an unrelated gesture is reaching the map`)
if (!(movedBelow > 50)) fail('c', `wheel below the map did not scroll the page (grid moved only ${movedBelow}px)`)
// ── CLAUSE F — THE WHEEL ESCAPES FROM *EVERY* COLUMN, NOT JUST THE ONE WE TEST ──────────────
// ★ WHY THIS CLAUSE EXISTS: clause C above wheels at x=150, the FACETS column. That coordinate
// is correct for what C asserts, but it made 16/16 certify a NARROWER claim than it appears to.
// MEASURED 2026-09-23, 1440x900, 12 wheel ticks x 350px = 4200px of delta at y=682:
// x=150 (facets col) scrollY 0 -> 2131 moved 2131px
// x=736 (TABLE col centre) scrollY 0 -> 0 moved 0px <-- dead zone
// x=1330 (rail col) scrollY 0 -> 2131 moved 2131px
// The table column is roughly the centre 60% of the screen — where a cursor naturally rests. A
// reader who finishes the table and keeps scrolling without moving the mouse sideways is stuck,
// which is the original complaint. Cause: .rd-grid is its own scroll surface with a virtualized
// scrollHeight of ~54,838px against a ~300px keyhole, so the overscroll-chain escape the design
// relies on can never fire — 4200px of wheel does not come close to bottoming out 54,538px.
//
// ★ THE GENERAL LESSON, which is why this is a CLAUSE and not a card: a single test coordinate
// grades one column. When the previously-failing point was moved to a passing column the gate
// went green while the defect stood — "the gate passes" and "the complaint is fixed" are
// different claims. Sweeping every column makes the gate fail wherever a user could actually be.
const colSweep = []
{
const cols = await page.evaluate(`(() => {
const shell = __walk('.rd-shell')[0]; if (!shell) return null
const pick = (r) => { const e = shell.querySelector('[data-region="' + r + '"]'); if (!e) return null
const b = e.getBoundingClientRect()
if (b.height < 40) return null
return { region: r, x: Math.round(b.left + b.width / 2), y: Math.round(b.top + b.height / 2) } }
return ['facets', 'table', 'rail'].map(pick).filter(Boolean)
})()`)
for (const col of (cols || [])) {
await page.evaluate(`(() => { window.scrollTo(0, 0); if (document.scrollingElement) document.scrollingElement.scrollTop = 0 })()`)
await page.waitForTimeout(500)
const y0 = await page.evaluate(`Math.round(window.scrollY)`)
// ★ WHEEL INSIDE THE COLUMN, NOT BELOW IT. belowY is deliberately past the band's bottom
// (clause C wants the page there), but a point past the bottom is OUTSIDE every column, so
// reusing it made all three columns measure the same open page and clause F passed for the
// wrong reason — elementFromPoint(736, belowY) returned the composite host, never the table.
// Each column supplies its OWN y: the vertical centre of its own rect.
await page.mouse.move(col.x, col.y)
for (let i = 0; i < 12; i++) { await page.mouse.wheel(0, 350); await page.waitForTimeout(50) }
await page.waitForTimeout(500)
const y1 = await page.evaluate(`Math.round(window.scrollY)`)
colSweep.push({ region: col.region, x: col.x, moved: y1 - y0 })
}
await page.evaluate(`(() => { window.scrollTo(0, 0); if (document.scrollingElement) document.scrollingElement.scrollTop = 0 })()`)
await reinject(page)
}
const stuck = colSweep.filter((c) => c.moved <= 50)
const okCols = colSweep.length > 0 && stuck.length === 0
record('F', `${TARGET} (wheel escapes every column)`, okCols,
colSweep.map((c) => `${c.region}@${c.x}:${c.moved}px`).join(' · ') || 'no columns found')
if (colSweep.length && stuck.length) {
fail('f', `a wheel over the ${stuck.map((s) => s.region).join(', ')} column(s) does not scroll the page ` +
`(moved ${stuck.map((s) => s.moved + 'px').join(', ')} after 4200px of wheel) — that column is a dead zone ` +
`for a reader whose cursor rests there`)
}
// ── CLAUSE D — THE ELEMENT SAYS WHAT IT IS SHOWING ──────────────────────────────────────────
// dashboard-plus-grid.vue:279 sets show-total=false on the grid child DELIBERATELY (the count
// lived twice — the grid's own "Total:" line 38px below the table's "Σ N groups" footer); the

View file

@ -345,6 +345,44 @@ function readComposes(dir, profileName) {
} catch { return null }
}
/**
* Extra profile-config keys a profile declares in its own <profile>.meta.json under
* `config`, merged into the standalone leaf page's BASE_CONFIG. Returns {} when absent.
*
* WHY THIS EXISTS (BUG-DOTLESS-MAPS-SHOW-TAP-A-DOT-HINT): the shared PinHint is mounted
* by ProfileLoader for every profile and opts out via profile-config `pinHint:false`
* (see library/components/hints/PinHint.vue). But the standalone leaf's config was a
* HARDCODED object here, so no profile could actually set that key — the documented
* opt-out existed with no channel to reach it, which is why PinHint's own header could
* record "an opt-out no profile has ever used". cities-internet/connectivity-map and
* gospel-resources/resources-map shade COUNTRY POLYGONS and render no dots at all, yet
* both showed "Tap a coloured dot" (MEASURED live, 2026-09-23) — a hint pointing at
* something the reader cannot see makes a working map look broken.
*
* Declarative on purpose: the generator stays free of a per-profile special case, and
* the same channel serves any future profile-config key without another edit here.
* `profile`/`tk`/`assetsBase`/`instanceId` are generator-owned and cannot be overridden.
*/
const RESERVED_CONFIG_KEYS = new Set(['profile', 'tk', 'assetsBase', 'instanceId', 'dataSource'])
function readProfileConfig(dir, profileName) {
try {
const metaPath = join(dir, `${profileName}.meta.json`)
if (!existsSync(metaPath)) return {}
const meta = JSON.parse(readFileSync(metaPath, 'utf8'))
const cfg = meta?.config
if (!cfg || typeof cfg !== 'object' || Array.isArray(cfg)) return {}
const out = {}
for (const [k, v] of Object.entries(cfg)) {
if (RESERVED_CONFIG_KEYS.has(k)) {
console.error(`[generate-tree] ${profileName}.meta.json: ignoring reserved config key "${k}"`)
continue
}
out[k] = v
}
return out
} catch { return {} }
}
/** Element tag names a bundle registers, read from its index.js (order preserved). */
function extractElements(indexJsPath) {
if (!existsSync(indexJsPath)) return []
@ -893,7 +931,10 @@ function profileHtml(b, profile, tokenUrlExpr, bakedToken) {
return mapPageHtml({
title: `${profile.profile} · ${b.bundle}`,
tag,
config: { profile: profile.profile, dataSource: 'pray-tools', instanceId: `${b.bundle}-${profile.profile}`, assetsBase: assetsBaseFor(3) },
// Profile-declared keys first, so the generator-owned ones always win (they are
// also filtered as reserved at read time — belt and braces, since a leaf whose
// `profile` or `assetsBase` got overwritten would silently render the wrong thing).
config: { ...(profile.extraConfig || {}), profile: profile.profile, dataSource: 'pray-tools', instanceId: `${b.bundle}-${profile.profile}`, assetsBase: assetsBaseFor(3) },
jsSrc: `../${b.bundle}.js`,
apiBaseUrl: DEST_CONFIG.apiBaseUrl,
tokenUrlExpr,
@ -1129,6 +1170,10 @@ function scanBundles() {
scriptUrl,
embed: { iframe: iframeEmbed, script: scriptEmbed },
...(instances.length ? { instances } : {}),
// Extra profile-config keys from <profile>.meta.json `config` (e.g. pinHint:false).
// GENERATOR-INTERNAL: stripped before the manifest is written (see writeManifestAndHtml)
// so the published manifest shape is unchanged.
extraConfig: readProfileConfig(dir, profileName),
...rest,
}
})
@ -1183,7 +1228,13 @@ function writeManifestAndHtml(root, bundles, tokenUrlExpr, bakedToken) {
embedBase: EMBED_BASE,
bundleCount: bundles.length,
profileCount: bundles.reduce((n, b) => n + b.profiles.length, 0),
bundles,
// `extraConfig` is a GENERATOR-INTERNAL carrier (<profile>.meta.json `config` → the leaf
// page's BASE_CONFIG) and is stripped here so the published manifest shape is unchanged.
// Consumers read the manifest as a contract; a new key would be a silent schema change.
bundles: bundles.map((b) => ({
...b,
profiles: b.profiles.map(({ extraConfig, ...p }) => p),
})),
}
mkdirSync(root, { recursive: true })
writeFileSync(join(root, 'manifest.json'), JSON.stringify(manifest, null, 2) + '\n')

View file

@ -83,7 +83,33 @@ import { useShadowStyles } from '@map/composables/useShadowStyles.js'
// Inject a bundle script exactly once per URL (deduped across all layout instances). Same helper the
// tabs layout + the SPA use — resolves after the IIFE's customElements.define has run.
const _stlLoaded = {}
function injectScriptOnce(url) {
// ★ ANCHOR A SIBLING-BUNDLE src TO THE TREE ROOT, NOT TO A GUESSED DEPTH.
// THE BUG (Driver-reported 2026-09-23, verbatim console text):
// Could not load <doxa-research-map> from ../../doxa-research-map/doxa-research-map.js
// A composite declares its children as `src: '../../<bundle>/<bundle>.js'`. That literal is correct
// from a ONE-deep leaf and wrong from a TWO-deep one, and the generator emits both:
// /doxa-maps/research-map-plus-grid/research-map-plus-grid/index.html ../../ -> /doxa-maps/ 200
// /doxa-maps/research-map-plus-grid/research-map-plus-grid/default/index.html ../../ -> /doxa-maps/research-map-plus-grid/ 404
// MEASURED live: both leaves serve 200, the child script 404s only from the deeper one. So a
// hardcoded relative path encodes an ASSUMPTION ABOUT THE HOST'S DEPTH that the host does not
// control and cannot see — the classic relative-path failure, and the reason it survived: the
// shallow leaf (which every gate happened to exercise) is genuinely fine.
// THE FIX: find the tree root from the document's own URL — everything up to and including the
// /doxa-maps/ segment — and rebuild the path from there. Depth-independent by construction rather
// than tuned, so adding a third leaf depth later cannot reintroduce this.
// Anything already absolute, protocol-relative or root-relative is passed through untouched; if no
// /doxa-maps/ segment is found (a foreign mount) the original string is returned, so this can only
// ever fix a path, never break one that was already working.
function resolveBundleSrc(url) {
if (!url || /^(https?:)?\/\//.test(url) || url.startsWith('/')) return url
const m = /^(.*\/doxa-maps\/)/.exec(window.location.pathname)
if (!m) return url
const bare = url.replace(/^(\.\.\/)+/, '').replace(/^\.\//, '')
return m[1] + bare
}
function injectScriptOnce(rawUrl) {
const url = resolveBundleSrc(rawUrl)
if (!url) return Promise.resolve()
if (_stlLoaded[url]) return _stlLoaded[url]
_stlLoaded[url] = new Promise((ok, fail) => {

View file

@ -17,7 +17,8 @@
* - one WebGL context (the ~16-cap rule; this is the app's only map pane)
* - tap-to-select with a bottom SHEET, not a hover popup — hover does not exist
* on a phone, and a hover-only map is an unusable map
* - the legend is a horizontal strip, not a floating panel that eats the screen
* - the legend is a BOUNDED panel (380px, left-anchored), clear of the control
* rail on the right. It was a full-width strip; that is what made it collide.
* - boundaries FETCHED at runtime (the IIFE inlines every import; 2.7MB of
* geojson in the bundle would block first paint)
*
@ -154,11 +155,24 @@ const fmt = (n) => (Number(n) || 0).toLocaleString()
useShadowStyles(
'.gm{position:absolute;inset:0;overflow:hidden}' +
'.gm-map{position:absolute;inset:0}' +
// LEGEND: a horizontal strip pinned to the top, so it never eats a phone screen
// the way a floating panel does.
'.gm-legend{position:absolute;top:8px;left:8px;right:8px;z-index:4;padding:8px 10px;' +
// LEGEND: a BOUNDED, left-anchored panel — NOT a full-width strip.
// It used to be `left:8px;right:8px`, which is a strip spanning the whole map
// (MEASURED 1264px of a 1280px map, 98.75%). That collided with the floating
// control rail by construction: MapToolbar is inset-inline-end:3px with 32px
// buttons, so ANY box reaching right:8px intersects it at every viewport. The
// rail's z-index:10 beats this z-index:4, so the buttons stayed clickable and
// the defect read as "the legend underlaps the controls" — reported 3x.
// The fix imitates the shared SemanticTreeLegend, which has always done it
// correctly: `left:8px; width:var(--map-legend-width,380px)`. Bounded and
// left-anchored cannot reach the right rail, so the two can never overlap
// again — a geometric guarantee, not a tuned z-index.
'.gm-legend{position:absolute;top:8px;left:8px;width:var(--map-legend-width,380px);' +
'max-width:calc(100% - 16px);box-sizing:border-box;z-index:4;padding:8px 10px;' +
'border-radius:10px;background:rgba(255,255,255,.94);color:#0f1419;' +
"font:12px/1.35 -apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;box-shadow:0 2px 10px rgba(0,0,0,.16)}" +
// Narrow screens: the rail is still on the right, so the legend must stop short
// of it rather than run under it. 46px = 32px button + 3px inset + clearance.
'@media(max-width:480px){.gm-legend{width:auto;right:46px}}' +
'.is-dark .gm-legend{background:rgba(19,32,25,.95);color:#e5e7eb}' +
'.gm-title{font-weight:700;font-size:12.5px;margin-bottom:5px}' +
'.gm-keys{display:flex;flex-wrap:wrap;gap:5px 12px}' +

View file

@ -17,7 +17,8 @@
* - one WebGL context (the ~16-cap rule; this is the app's only map pane)
* - tap-to-select with a bottom SHEET, not a hover popup — hover does not exist
* on a phone, and a hover-only map is an unusable map
* - the legend is a horizontal strip, not a floating panel that eats the screen
* - the legend is a BOUNDED panel (380px, left-anchored), clear of the control
* rail on the right. It was a full-width strip; that is what made it collide.
* - boundaries FETCHED at runtime (the IIFE inlines every import; 2.7MB of
* geojson in the bundle would block first paint)
*
@ -320,11 +321,28 @@ const fmt = (n) => (Number(n) || 0).toLocaleString()
useShadowStyles(
'.gm{position:absolute;inset:0;overflow:hidden}' +
'.gm-map{position:absolute;inset:0}' +
// LEGEND: a horizontal strip pinned to the top, so it never eats a phone screen
// the way a floating panel does.
'.gm-legend{position:absolute;top:8px;left:8px;right:8px;z-index:4;padding:8px 10px;' +
// LEGEND: a BOUNDED, left-anchored panel — NOT a full-width strip.
// It used to be `left:8px;right:8px`, which is a strip spanning the whole map
// (MEASURED 1264px of a 1280px map, 98.75%). That collided with the floating
// control rail by construction: MapToolbar is inset-inline-end:3px with 32px
// buttons, so ANY box reaching right:8px intersects it at every viewport. This
// legend is the TALLER of the two siblings (127px — it carries the mode
// toggle), so it ran under FOUR of the five buttons where cities ran under two.
// The rail's z-index:10 beats this z-index:4, so the buttons stayed clickable
// and the defect read as "the legend underlaps the controls" — reported 3x.
// The fix imitates the shared SemanticTreeLegend, which has always done it
// correctly: `left:8px; width:var(--map-legend-width,380px)`. Bounded and
// left-anchored cannot reach the right rail, so the two can never overlap
// again — a geometric guarantee, not a tuned z-index.
// KEEP THIS BYTE-IDENTICAL to cities-internet/connectivity-map.vue: they are
// clones and the whole point of fixing them together is that they not diverge.
'.gm-legend{position:absolute;top:8px;left:8px;width:var(--map-legend-width,380px);' +
'max-width:calc(100% - 16px);box-sizing:border-box;z-index:4;padding:8px 10px;' +
'border-radius:10px;background:rgba(255,255,255,.94);color:#0f1419;' +
"font:12px/1.35 -apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;box-shadow:0 2px 10px rgba(0,0,0,.16)}" +
// Narrow screens: the rail is still on the right, so the legend must stop short
// of it rather than run under it. 46px = 32px button + 3px inset + clearance.
'@media(max-width:480px){.gm-legend{width:auto;right:46px}}' +
'.is-dark .gm-legend{background:rgba(19,32,25,.95);color:#e5e7eb}' +
'.gm-title{font-weight:700;font-size:12.5px;margin-bottom:5px}' +
'.gm-keys{display:flex;flex-wrap:wrap;gap:5px 12px}' +

File diff suppressed because one or more lines are too long

View file

@ -40,7 +40,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <dashboard-plus-grid> custom element. -->
<script src="../../dashboard-plus-grid.js" integrity="sha384-zUvuNNWNZdbjdsO1BJ6Wa+0mZ5HE4FEDpRRbWK2DFU52H9kh3zdY2SaQeY6HfeMD" crossorigin="anonymous"></script>
<script src="../../dashboard-plus-grid.js" integrity="sha384-tuxsA7/uAliDjcm0nKMy1ERDb/quhsf0erb9ZVW2wRKH5gQg9zBqcso3Byd3V81H" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -40,7 +40,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <dashboard-plus-grid> custom element. -->
<script src="../dashboard-plus-grid.js" integrity="sha384-zUvuNNWNZdbjdsO1BJ6Wa+0mZ5HE4FEDpRRbWK2DFU52H9kh3zdY2SaQeY6HfeMD" crossorigin="anonymous"></script>
<script src="../dashboard-plus-grid.js" integrity="sha384-tuxsA7/uAliDjcm0nKMy1ERDb/quhsf0erb9ZVW2wRKH5gQg9zBqcso3Byd3V81H" 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

@ -193,7 +193,7 @@
"bundle": "dashboard-plus-grid",
"scriptUrl": "./dashboard-plus-grid/dashboard-plus-grid.js",
"output": "doxa-maps/dashboard-plus-grid/dashboard-plus-grid.js",
"integrity": "sha384-zUvuNNWNZdbjdsO1BJ6Wa+0mZ5HE4FEDpRRbWK2DFU52H9kh3zdY2SaQeY6HfeMD",
"integrity": "sha384-tuxsA7/uAliDjcm0nKMy1ERDb/quhsf0erb9ZVW2wRKH5gQg9zBqcso3Byd3V81H",
"elements": [
"dashboard-plus-grid"
],
@ -213,7 +213,7 @@
"scriptUrl": "./dashboard-plus-grid/dashboard-plus-grid.js",
"embed": {
"iframe": "<iframe src=\"./dashboard-plus-grid/dashboard-plus-grid/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=\"./dashboard-plus-grid/dashboard-plus-grid.js\" integrity=\"sha384-zUvuNNWNZdbjdsO1BJ6Wa+0mZ5HE4FEDpRRbWK2DFU52H9kh3zdY2SaQeY6HfeMD\" crossorigin=\"anonymous\"></script>\n<dashboard-plus-grid profile-config='{\"profile\":\"dashboard-plus-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></dashboard-plus-grid>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./dashboard-plus-grid/dashboard-plus-grid.js\" integrity=\"sha384-tuxsA7/uAliDjcm0nKMy1ERDb/quhsf0erb9ZVW2wRKH5gQg9zBqcso3Byd3V81H\" crossorigin=\"anonymous\"></script>\n<dashboard-plus-grid profile-config='{\"profile\":\"dashboard-plus-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></dashboard-plus-grid>"
},
"instances": [
{
@ -904,7 +904,7 @@
"bundle": "research-map-plus-grid",
"scriptUrl": "./research-map-plus-grid/research-map-plus-grid.js",
"output": "doxa-maps/research-map-plus-grid/research-map-plus-grid.js",
"integrity": "sha384-W3eI3zepAYbIa1bQfJTtvzU53KWPQEg8g4lVKmp3FtW7kNXOoKE2ny1JDjZ1L+MJ",
"integrity": "sha384-RD6XMWZvRaam/1XF9HAsXE+XutNjsboygfmxLMP9JJEnZ0LdIHqR+gYGWE1Y6Gzq",
"elements": [
"research-map-plus-grid"
],
@ -924,7 +924,7 @@
"scriptUrl": "./research-map-plus-grid/research-map-plus-grid.js",
"embed": {
"iframe": "<iframe src=\"./research-map-plus-grid/research-map-plus-grid/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=\"./research-map-plus-grid/research-map-plus-grid.js\" integrity=\"sha384-W3eI3zepAYbIa1bQfJTtvzU53KWPQEg8g4lVKmp3FtW7kNXOoKE2ny1JDjZ1L+MJ\" crossorigin=\"anonymous\"></script>\n<research-map-plus-grid profile-config='{\"profile\":\"research-map-plus-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-map-plus-grid>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-map-plus-grid/research-map-plus-grid.js\" integrity=\"sha384-RD6XMWZvRaam/1XF9HAsXE+XutNjsboygfmxLMP9JJEnZ0LdIHqR+gYGWE1Y6Gzq\" crossorigin=\"anonymous\"></script>\n<research-map-plus-grid profile-config='{\"profile\":\"research-map-plus-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-map-plus-grid>"
},
"instances": [
{
@ -948,7 +948,7 @@
"bundle": "stack-demo",
"scriptUrl": "./stack-demo/stack-demo.js",
"output": "doxa-maps/stack-demo/stack-demo.js",
"integrity": "sha384-GQTUiygn0aS3lHIfW6HIYA1Ko8P2KZr0iUicOp+xDFLNw2gaGmLCZdwa5/cUYeUI",
"integrity": "sha384-g9iK/zw5CvcyhNWdWHe8n2S/O22tv3PhQhoCe45ArdX5dL2YRBBRl/hFjktel1UU",
"elements": [
"stack-demo"
],
@ -968,7 +968,7 @@
"scriptUrl": "./stack-demo/stack-demo.js",
"embed": {
"iframe": "<iframe src=\"./stack-demo/stack-demo/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=\"./stack-demo/stack-demo.js\" integrity=\"sha384-GQTUiygn0aS3lHIfW6HIYA1Ko8P2KZr0iUicOp+xDFLNw2gaGmLCZdwa5/cUYeUI\" crossorigin=\"anonymous\"></script>\n<stack-demo profile-config='{\"profile\":\"stack-demo\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></stack-demo>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./stack-demo/stack-demo.js\" integrity=\"sha384-g9iK/zw5CvcyhNWdWHe8n2S/O22tv3PhQhoCe45ArdX5dL2YRBBRl/hFjktel1UU\" crossorigin=\"anonymous\"></script>\n<stack-demo profile-config='{\"profile\":\"stack-demo\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></stack-demo>"
},
"instances": [
{

File diff suppressed because one or more lines are too long

View file

@ -37,7 +37,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <research-map-plus-grid> custom element. -->
<script src="../../research-map-plus-grid.js" integrity="sha384-W3eI3zepAYbIa1bQfJTtvzU53KWPQEg8g4lVKmp3FtW7kNXOoKE2ny1JDjZ1L+MJ" crossorigin="anonymous"></script>
<script src="../../research-map-plus-grid.js" integrity="sha384-RD6XMWZvRaam/1XF9HAsXE+XutNjsboygfmxLMP9JJEnZ0LdIHqR+gYGWE1Y6Gzq" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -37,7 +37,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <research-map-plus-grid> custom element. -->
<script src="../research-map-plus-grid.js" integrity="sha384-W3eI3zepAYbIa1bQfJTtvzU53KWPQEg8g4lVKmp3FtW7kNXOoKE2ny1JDjZ1L+MJ" crossorigin="anonymous"></script>
<script src="../research-map-plus-grid.js" integrity="sha384-RD6XMWZvRaam/1XF9HAsXE+XutNjsboygfmxLMP9JJEnZ0LdIHqR+gYGWE1Y6Gzq" 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

@ -37,7 +37,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <stack-demo> custom element. -->
<script src="../stack-demo.js" integrity="sha384-GQTUiygn0aS3lHIfW6HIYA1Ko8P2KZr0iUicOp+xDFLNw2gaGmLCZdwa5/cUYeUI" crossorigin="anonymous"></script>
<script src="../stack-demo.js" integrity="sha384-g9iK/zw5CvcyhNWdWHe8n2S/O22tv3PhQhoCe45ArdX5dL2YRBBRl/hFjktel1UU" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -37,7 +37,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <stack-demo> custom element. -->
<script src="../../stack-demo.js" integrity="sha384-GQTUiygn0aS3lHIfW6HIYA1Ko8P2KZr0iUicOp+xDFLNw2gaGmLCZdwa5/cUYeUI" crossorigin="anonymous"></script>
<script src="../../stack-demo.js" integrity="sha384-g9iK/zw5CvcyhNWdWHe8n2S/O22tv3PhQhoCe45ArdX5dL2YRBBRl/hFjktel1UU" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order: