Merge branch 'main' of https://git.nuxtbase.app/doxa-life/doxa-maps into bundler-1/work
Some checks are pending
build-gate / build (push) Waiting to run

# Conflicts:
#	public/doxa-maps/index.html
This commit is contained in:
doxa-maps-88655c70 2026-09-24 21:57:12 +00:00
commit 661133dd5c
71 changed files with 1126 additions and 679 deletions

View file

@ -125,6 +125,31 @@ owe a complete audit.
★ **Read the image when the deliverable is visual.** Numbers have certified visually broken output
in this repo more than once — a green generator is not a look-at-it.
## A pipe hides a failure
A pipeline's exit status is its LAST command's. So this publishes a STALE bundle — new
source, old `.js` — and says nothing:
```sh
BUNDLE=x bun x vite build 2>&1 | tail -2 && bun run tree # tail succeeded, so && proceeds
```
Measured on this machine, all three states:
| Form | On a failed build |
|---|---|
| `build 2>&1 \| tail -2 && next` | **reports success** and runs `next` |
| `set -o pipefail` before it | stops, correctly |
| `build >/dev/null 2>&1 && next` | stops, correctly — no pipe, so the status is the build's |
Every gate stays green afterwards, because `check:sri` proves the leaf and the bundle agree
with **each other**, not with the source. The same shape bit a `git merge` in this repo:
`if ! git merge … | tail` tested the pipe, so a conflicted merge read as clean.
**Rule:** never put a build or a merge on the left of a pipe you then branch on. Redirect
instead of piping, or `set -o pipefail` first. If you want short output AND a reliable
status, capture to a file and `tail` the file afterwards.
## Landing on main: the shape of the lock
`main` is shared and serialized by `main-capture`. The ordering is load-bearing and BOTH

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1,018 KiB

View file

@ -0,0 +1,42 @@
{
"cap1-single-element": {
"bundle": "all-people-groups",
"sha256": "d48c17dc369294301d17a4d29fc517cc7c2c2b18561b6d416ae02a9fbfb60c33"
},
"cap2-adoption": {
"bundle": "doxa-simple-map",
"sha256": "a94db2c47d598dbc59daec6903afaac93862add9c0b7a532891b9c3c0608ce02"
},
"cap2-engagement": {
"bundle": "doxa-simple-map",
"sha256": "a94db2c47d598dbc59daec6903afaac93862add9c0b7a532891b9c3c0608ce02"
},
"cap2-prayer": {
"bundle": "doxa-simple-map",
"sha256": "a94db2c47d598dbc59daec6903afaac93862add9c0b7a532891b9c3c0608ce02"
},
"cap3-monolithic-research": {
"bundle": "doxa-research-map",
"sha256": "eb305958dffd4a067de6f00155c3f396452adfc6d20e842cc9ae4f921e54f44a"
},
"cap4-upg100-list": {
"bundle": "my-upg-100-list",
"sha256": "ff98986714934370a76742e3aa492a15ce67b959888f03092a453595103313d2"
},
"cap4-upg100-mfe": {
"bundle": "my-upg-100-list",
"sha256": "ff98986714934370a76742e3aa492a15ce67b959888f03092a453595103313d2"
},
"cap4-upg100-poster-wall": {
"bundle": "my-upg-100-list",
"sha256": "ff98986714934370a76742e3aa492a15ce67b959888f03092a453595103313d2"
},
"cap5-doxa-maps-spa": {
"bundle": "doxa-maps-spa",
"sha256": "c7b1aad1e3bd4bbe936858f2159c566c1ef2e24fc9636a3446b1e10723d86502"
},
"cap5-plugin-studio": {
"bundle": "plugin-studio",
"sha256": "c3d32d94d2df407fc35cea91faf0d46670abf52a63d37000486760361a588a5c"
}
}

View file

@ -68,9 +68,9 @@ async function measure(page, label, activateTab) {
if (activateTab) {
// Heavy tabs lazy-mount on activation: before the click nothing exists and every query returns
// 0, which reads as "no filters here" rather than "not mounted yet".
const idx = await page.evaluate(`(${WALK})('.vtl-tab').findIndex(t=>${activateTab}.test(t.textContent||''))`)
const idx = await page.evaluate(`(${WALK})('.vtl-tab, .st-row[role=treeitem]:not(.st-sec)').findIndex(t=>${activateTab}.test(t.textContent||''))`)
if (idx < 0) return { label, error: 'no tab matched ' + activateTab }
await page.evaluate(`(${WALK})('.vtl-tab')[${idx}].click()`)
await page.evaluate(`(${WALK})('.vtl-tab, .st-row[role=treeitem]:not(.st-sec)')[${idx}].click()`)
await page.waitForTimeout(9000)
}

View file

@ -628,7 +628,7 @@ async function runShellBestEffort() {
// already proved (dashboard-plus-grid / "Big Picture" / "Dashboard"), rather than assuming a
// fixed label or index — resilient to the three-section restructure landing mid-flight.
const tab = await page.evaluate(`(() => {
const cands = __walk('.vtl-tab');
const cands = __walk('.vtl-tab, .st-row[role=treeitem]:not(.st-sec)');
const hit = cands.find(t => /dashboard.plus.grid|big picture|dashboard/i.test((t.textContent||'')));
if (!hit) return null;
hit.click();

View file

@ -75,9 +75,9 @@ async function hostRun(browser, label, url, activate) {
// Heavy tabs are lazy-mounted on activation, so nothing exists until a tab is clicked. A flat
// query before this point returns 0 triggers and reads as "no dropdowns here" — which is how
// a host can silently drop out of coverage while the gate still reports success.
const idx = await page.evaluate(`(${DEEP})('.vtl-tab').findIndex(t=>${activate}.test(t.textContent||''))`)
const idx = await page.evaluate(`(${DEEP})('.vtl-tab, .st-row[role=treeitem]:not(.st-sec)').findIndex(t=>${activate}.test(t.textContent||''))`)
if (idx < 0) return { label, error: 'no tab matched ' + activate }
await page.evaluate(`(${DEEP})('.vtl-tab')[${idx}].click()`)
await page.evaluate(`(${DEEP})('.vtl-tab, .st-row[role=treeitem]:not(.st-sec)')[${idx}].click()`)
await page.waitForTimeout(9000)
}
const n = await page.evaluate(`(${DEEP})('.filter-dropdown__trigger').length`)

View file

@ -25,6 +25,11 @@
* Shots whose path is not a /doxa-maps/ leaf (the synthetic iframe host) are REPORTED as skipped
* rather than dropped — an unchecked shot must be visible, not invisible.
*
* CONTENT FIRST, CLOCKS ONLY AS A FALLBACK. readme-shots writes shot-bundles.json: for each shot,
* the sha256 of the bundle it was taken from. A matching hash means the picture shows exactly the
* bundle built now; a different one means it is stale. No date is involved, so a clone, a rebase
* or a retake that came out byte-identical cannot fool it. Shots with no record yet fall back to:
*
* WHICH CLOCK: GIT, NOT THE FILESYSTEM, for any file that is committed and unmodified.
* This compared mtimes, and a clone (or a new worktree) writes files in whatever order git checks
* them out — so every fresh checkout failed, pristine main included (11 shots "2s behind" their
@ -33,11 +38,12 @@
* tarball) falls back to its mtime, because then the working copy IS the newest version.
*
* NEGATIVE-TESTED: appending a byte to public/doxa-maps/doxa-simple-map/doxa-simple-map.js makes
* this FAIL naming the three cap2-* shots (a dirty file is dated by mtime); reverting it returns it
* to PASS. A bare `touch` no longer fails it — a touch changes no content, so nothing is stale.
* this FAIL naming exactly the three cap2-* shots (their recorded hash no longer matches);
* reverting it returns it to PASS. A bare `touch` does not fail it: it changes no content.
*/
import { readFileSync, existsSync, statSync } from 'node:fs'
import { execFileSync } from 'node:child_process'
import { createHash } from 'node:crypto'
import { resolve, dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
@ -51,6 +57,9 @@ if (!existsSync(SHOTS_SRC) || !existsSync(OUT)) {
process.exit(0)
}
const SHOT_BUNDLES = join(OUT, 'shot-bundles.json')
const recorded = existsSync(SHOT_BUNDLES) ? JSON.parse(readFileSync(SHOT_BUNDLES, 'utf8')) : {}
// Dirty = modified or untracked under the two trees compared. null = git unavailable -> mtimes only.
const git = (...a) => execFileSync('git', ['-C', ROOT, ...a], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] })
let dirty = null
@ -86,6 +95,18 @@ for (const { name, path } of shots) {
if (!existsSync(bundle)) { skipped.push(`${name} (bundle ${m[1]} not built)`); continue }
checked++
// FIRST CHOICE: content, not clocks. readme-shots records the sha256 of the bundle each picture
// was taken from. Same hash = the picture shows exactly this bundle, however old either file's
// date. A different hash = the bundle changed since, so the picture is stale. This also covers
// a retake that came out byte-identical, which no clock can see: git dates an unchanged PNG by
// its old commit, and the gate called a correct, just-taken picture stale.
const rec = recorded[name]
if (rec && rec.bundle === m[1]) {
const now = createHash('sha256').update(readFileSync(bundle)).digest('hex')
if (now !== rec.sha256) stale.push(`${name}.png was shot from a different ${m[1]} bundle than the one built now`)
continue
}
// FALLBACK, for a shot with no record yet: clocks.
const b = stamp(bundle), p = stamp(png)
if (b.ms > p.ms) {
stale.push(`${name}.png is OLDER than the bundle it shows (${m[1]}) — ` +
@ -109,4 +130,4 @@ if (missing.length || stale.length) {
console.error(' A stale screenshot looks exactly like a current one — that is why this is a gate.')
process.exit(1)
}
console.log('SHOT-FRESHNESS: PASS — every screenshot is newer than the bundle it shows.')
console.log('SHOT-FRESHNESS: PASS — every screenshot shows the bundle that is built now.')

View file

@ -12,7 +12,9 @@
*/
import { chromium } from 'playwright'
import { createServer } from 'node:http'
import { readFile, stat, mkdir } from 'node:fs/promises'
import { readFile, stat, mkdir, writeFile } from 'node:fs/promises'
import { createHash } from 'node:crypto'
import { existsSync, readFileSync } from 'node:fs'
import { join, extname, resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
@ -86,6 +88,10 @@ const browser = await chromium.launch({ args: [
// ★ A SHOT THAT FAILS MUST NOT SILENTLY PRODUCE A README IMAGE. Collected and asserted at the end.
const failures = []
// documentation/readme-images/shot-bundles.json: shot name -> the sha256 of the bundle it was shot
// from. Merged, so an ONLY= run updates just the shots it took.
const SHOT_BUNDLES = resolve(HERE, '..', 'documentation', 'readme-images', 'shot-bundles.json')
const shotBundles = existsSync(SHOT_BUNDLES) ? JSON.parse(readFileSync(SHOT_BUNDLES, 'utf8')) : {}
const ONLY = process.env.ONLY ? process.env.ONLY.split(',') : null
// mapbox canvas selector to wait on (leaf pages mount a custom element with a canvas)
@ -183,6 +189,13 @@ async function shot(name, path, {
if (clip) opts.clip = clip
await page.screenshot(opts)
console.log(' OK', name + '.png', `<- ${path} (${ready} elements)`)
// Record WHICH BUNDLE this picture is of, by content hash, so check:shots can tell a current
// picture from a stale one without trusting any clock (see SHOT_BUNDLES below).
const lm = path.match(/^\/doxa-maps\/([^/]+)\//)
const bundleJs = lm && join(ROOT, 'doxa-maps', lm[1], `${lm[1]}.js`)
if (bundleJs && existsSync(bundleJs)) {
shotBundles[name] = { bundle: lm[1], sha256: createHash('sha256').update(readFileSync(bundleJs)).digest('hex') }
}
} catch (e) {
console.log(' ERR', name, e.message.split('\n')[0])
failures.push(name + ': ' + e.message.split('\n')[0])
@ -280,15 +293,24 @@ await shot('cap5-plugin-studio', '/doxa-maps/plugin-studio/plugin-studio/index.h
before: async (page) => {
// The rail lives inside the element's shadow root, so a light-DOM selector finds nothing —
// click through an evaluated deep walk instead (the same seen-Set walk the readiness guard uses).
// The rail is an Explorer tree (f63d75e): its plugin rows are .st-row[role=treeitem], and a
// .st-sec row is a section heading, not a plugin. The older .vtl-tab rail is kept as a
// fallback. Without a tree row, the walk would reach the NESTED gallery's own .vtl-tab buttons
// instead, and click a tab inside the plugin rather than a plugin in the rail.
await page.evaluate(() => {
const seen = new Set(); let first = null
;(function w(n) {
if (!n || seen.has(n) || first) return
seen.add(n)
if (n.tagName === 'BUTTON' && /vtl-tab\b/.test((n.className || '').toString())) { first = n; return }
if (n.shadowRoot) w(n.shadowRoot)
for (const c of (n.children || [])) w(c)
})(document.body)
const find = (test) => {
const seen = new Set(); let hit = null
;(function w(n) {
if (!n || seen.has(n) || hit) return
seen.add(n)
if (n.nodeType === 1 && test(n)) { hit = n; return }
if (n.shadowRoot) w(n.shadowRoot)
for (const c of (n.children || [])) w(c)
})(document.body)
return hit
}
const first = find((n) => n.matches('.st-row[role="treeitem"]:not(.st-sec)'))
|| find((n) => n.tagName === 'BUTTON' && /vtl-tab\b/.test((n.className || '').toString()))
if (first) first.click()
})
await page.waitForTimeout(3000) // let the selected element mount before the main wait
@ -352,6 +374,7 @@ if (process.env.DXLIFE_URL) {
await browser.close()
server.close()
await writeFile(SHOT_BUNDLES, JSON.stringify(Object.fromEntries(Object.entries(shotBundles).sort()), null, 1) + '\n')
// ★ EXIT NON-ZERO ON ANY FAILED SHOT. Previously this printed DONE unconditionally, so a run
// where every map came back blank still reported success and the stale PNGs stayed in documentation/.

View file

@ -109,7 +109,7 @@ try {
await page.evaluate(DEEP)
const state = await page.evaluate(() => ({
present: !!document.querySelector('plugin-studio'),
tabs: window.__q('.vtl-tab').length,
tabs: window.__q('.vtl-tab, .st-row[role=treeitem]:not(.st-sec)').length,
}))
// ── assertion 2: the element mounts AND its tag is defined ───────────────────
@ -148,6 +148,10 @@ if (state.tabs === 0) {
// and renders nothing scored identically to one rendering 1,397 nodes. That is the precise
// "mounts is not works" trap this file's own header warns about, encoded into the check anyway.
// The fix is to walk INTO the mounted element (through its shadow root) and require real depth.
// ★ "TABS" INCLUDES THE STUDIO'S TREE ROWS (2026-09-24). plugin-studio's rail is an Explorer tree
// (src/StudioTree.vue), not VerticalTabsLayout tabs, so every selector here matches BOTH shapes:
// '.vtl-tab' (nested shells like doxa-maps-spa) and the tree's treeitems minus section headers (a
// section header only folds; it opens no plugin, so sampling one would report a false "no content").
const SAMPLE_TABS = Number(process.env.SMOKE_TABS || 4)
const MIN_DEPTH = Number(process.env.SMOKE_MIN_DEPTH || 5)
@ -161,7 +165,7 @@ for (let i = 0; i < Math.min(SAMPLE_TABS, state.tabs); i++) {
const results = []
for (const i of idxs) {
await page.evaluate(DEEP)
await page.evaluate((n) => { const t = window.__q('.vtl-tab')[n]; if (t) t.click() }, i)
await page.evaluate((n) => { const t = window.__q('.vtl-tab, .st-row[role=treeitem]:not(.st-sec)')[n]; if (t) t.click() }, i)
// POLL until the pane has real content rather than sleeping a fixed amount. A fixed wait forces an
// ugly trade: long enough for the slowest tab makes the gate too slow to run on every edit, and
// short enough to be fast marks a slow-mounting tab BROKEN. That false positive is the worse
@ -183,7 +187,7 @@ for (const i of idxs) {
}
await page.evaluate(DEEP)
results.push(await page.evaluate((n) => {
const tab = window.__q('.vtl-tab')[n]
const tab = window.__q('.vtl-tab, .st-row[role=treeitem]:not(.st-sec)')[n]
const label = (tab && tab.textContent || '').replace(/\s+/g, ' ').trim().slice(0, 24)
const p = window.__q('.ps-pane').find(x => !x.hasAttribute('hidden'))
if (!p) return { label, none: true }

View file

@ -96,7 +96,7 @@ const run = async () => {
// query returns 0 — which reads as "no bug" if you stop here. This is the step both previous
// investigations skipped.
const tab = await page.evaluate(`(() => { ${WALK}
for (const t of __walk('.vtl-tab')) {
for (const t of __walk('.vtl-tab, .st-row[role=treeitem]:not(.st-sec)')) {
if ((t.textContent || '').toLowerCase().includes('dashboard-plus-grid')) { t.click(); return t.textContent.trim() }
}
return null
@ -194,7 +194,7 @@ const run = async () => {
await page2.goto(BASE + SHELL, { waitUntil: 'load' })
await page2.waitForTimeout(6000)
const mapTab = await page2.evaluate(`(() => { ${WALK}
for (const t of __walk('.vtl-tab')) {
for (const t of __walk('.vtl-tab, .st-row[role=treeitem]:not(.st-sec)')) {
if ((t.textContent || '').toLowerCase().includes('doxa-research-map')) { t.click(); return t.textContent.trim() }
}
return null

View file

@ -24,14 +24,6 @@
(and optional `open:false`) renders a COLLAPSIBLE section (click the heading — chevron rotates); a
plain group is an always-open labelled section (the "Subscriptions" style). A folder heading also
shows the group's `badge` + `count` when given.
Two optional row fields let a rail show a TREE whose nodes repeat elsewhere:
depth : indent level (0/omitted = flush) — draws the row nested under the row above it
selects : the `key` of the canonical row this one stands for. An ALIAS row has its own unique
`key` (for v-for) but clicking it selects `selects`, it highlights whenever that key is
active, and it is skipped by keyboard nav so arrow keys visit each canonical row once.
branch : an id that makes this row FOLDABLE — a chevron beside it shows/hides the rows below.
Closed by default; picking the row also opens it.
ancestors : the `branch` ids above this row; it shows only while ALL of them are open.
Selection is controlled via v-model:active (the active item's `key`). The host owns pane content
and mount logic — this component only paints the SHELL (top bar + rail + a content frame) and
exposes slots so each app keeps its unique chrome + its own mount machinery:
@ -39,6 +31,9 @@
#meta — right side of the top bar (e.g. a count, a language switcher)
#toolbar — a full-width strip under the top bar, above the content (e.g. an action bar)
#default — the content pane(s); the host renders its own panes and toggles them on `active`
#rail — (optional) replaces the rail body below the collapse toggle, for a host whose
navigation is not a list of tabs. Slot props: collapsed (desktop slim state), expand(),
pick(key). plugin-studio uses it for its Explorer tree.
★ NESTED-ELEMENT HOSTING (opt-in, LAZY). A row/item may instead ask THIS layout to host a nested
custom element for its tab — so a shell can compose other element-plugins as its tabs. Add to a
@ -128,7 +123,8 @@
an off-canvas drawer parked at translateX(-100%) and slid in by the `nav-open` class. Because it
is never display:none, a mobile-closed drawer can no longer strand the rail after a resize back
to desktop (the old cross-breakpoint bug). `aria-hidden` mirrors real visibility for a11y. -->
<nav class="vtl-nav" :aria-label="navLabel" :aria-hidden="String(!railShown)" role="tablist" aria-orientation="vertical">
<nav class="vtl-nav" :aria-label="navLabel" :aria-hidden="String(!railShown)"
:role="$slots.rail ? null : 'tablist'" :aria-orientation="$slots.rail ? null : 'vertical'">
<!-- Rail toggle: always visible, collapses the rail (slim icon rail on desktop; off-canvas
drawer on mobile). The chevron points the way the click will move the rail. -->
<button
@ -140,8 +136,13 @@
<span class="vtl-collapse-tx">Collapse</span>
</button>
<!-- #rail: a host that needs MORE than tabs (e.g. plugin-studio's tree) supplies the whole rail
body. The layout keeps the frame — collapse toggle, drawer, slim strip — and hands the slot
what it needs to cooperate with it. The nav drops role=tablist, since the slot brings its
own role (a tree is not a tablist). -->
<slot v-if="$slots.rail" name="rail" :collapsed="railCollapsed && !narrow" :expand="expandRail" :pick="pick" />
<!-- GROUPED shape: labelled sections (always-open) or collapsible folders. -->
<template v-if="groups && groups.length">
<template v-else-if="groups && groups.length">
<div class="vtl-grp" v-for="grp in groups" :key="grp.key">
<!-- FOLDER heading = a button that toggles open/closed (chevron rotates). -->
<button
@ -164,24 +165,13 @@
</div>
<div class="vtl-rows" v-show="!grp.folder || !isFolderClosed(grp)">
<div class="vtl-rowwrap" role="presentation" v-for="row in grp.rows" :key="row.key"
v-show="rowShown(row)">
<!-- BRANCH toggle: folds this row's sub-rows (see `branch` / `ancestors`). A sibling of the
tab, never inside it — a button may not contain a button. -->
<button v-if="row.branch" type="button" class="vtl-twisty"
:aria-expanded="String(isBranchOpen(row.branch))"
:aria-label="(isBranchOpen(row.branch) ? 'Hide' : 'Show') + ' what ' + row.label + ' is made of'"
@click="toggleBranch(row.branch)">
<span class="vtl-chev" :class="{ open: isBranchOpen(row.branch) }" aria-hidden="true" v-html="iconSvg('chevron')"></span>
</button>
<button
type="button" role="tab"
class="vtl-tab" :class="{ active: target(row) === active, alias: !!row.selects, 'has-branch': !!row.branch }"
:data-depth="row.depth || null" :style="row.depth ? { '--vtl-depth': row.depth } : null"
:aria-selected="String(target(row) === active)"
:tabindex="!row.selects && row.key === active ? 0 : -1"
:ref="el => { if (!row.selects) registerTab(row.key, el) }"
@click="pick(target(row), row)" @keydown="onKey($event)">
v-for="row in grp.rows" :key="row.key" type="button" role="tab"
class="vtl-tab" :class="{ active: row.key === active }"
:aria-selected="String(row.key === active)"
:tabindex="row.key === active ? 0 : -1"
:ref="el => registerTab(row.key, el)"
@click="pick(row.key)" @keydown="onKey($event)">
<span class="vtl-tab-ic" aria-hidden="true" v-if="row.icon" v-html="iconSvg(row.icon)"></span>
<span class="vtl-tab-emoji" aria-hidden="true" v-else-if="row.emoji">{{ row.emoji }}</span>
<!-- Slim-rail fallback (desktop collapsed only): a row with NEITHER icon NOR emoji would
@ -193,7 +183,6 @@
<span class="vtl-tab-sub" v-if="row.sub"><span v-html="row.sub"></span></span>
</span>
</button>
</div>
</div>
</div>
</template>
@ -380,13 +369,6 @@ const VTL_SHADOW_CSS = `
THIS RULE EXISTS TWICE (VTL_SHADOW_CSS and the scoped style block) and the two copies must stay
byte-identical — edit both or neither. */
.vtl-tab { display:flex; align-items:center; gap:11px; width:100%; min-height:48px; text-align:left; border:0; background:none; color:var(--vtl-muted); padding:8px 12px; cursor:pointer; border-radius:var(--vtl-radius,8px); font:inherit; margin:0; transition:background .13s ease,color .13s ease; }
.vtl-tab[data-depth] { padding-left:calc(12px + var(--vtl-depth) * 16px); }
.vtl-tab[data-depth]::before { content:''; flex:0 0 auto; align-self:stretch; width:0; margin-right:-3px; border-left:2px solid var(--vtl-accent); opacity:.45; }
.vtl-rowwrap { position:relative; }
.vtl-twisty { position:absolute; z-index:1; right:4px; top:50%; transform:translateY(-50%); width:28px; height:28px; display:inline-flex; align-items:center; justify-content:center; padding:0; border:0; border-radius:8px; background:none; color:var(--vtl-muted); cursor:pointer; }
.vtl-twisty:hover { background:var(--vtl-accented); color:#3b463d; }
.vtl-twisty:focus-visible { outline:2px solid var(--vtl-accent); outline-offset:-2px; }
.vtl-tab.has-branch { padding-right:36px; }
.vtl-tab:hover { background:var(--vtl-accented); color:#3b463d; }
.vtl-tab:focus-visible { outline:2px solid var(--vtl-accent); outline-offset:-2px; }
.vtl-tab.active { background:var(--vtl-accented); color:#3b463d; font-weight:500; }
@ -481,8 +463,6 @@ const VTL_SHADOW_CSS = `
.vtl.rail-collapsed .vtl-tab-text { display:none; }
.vtl.rail-collapsed .vtl-tab-emoji { font-size:19px; }
.vtl.rail-collapsed .vtl-tab-fallback { display:inline-flex; }
.vtl.rail-collapsed .vtl-tab[data-depth]::before { display:none; }
.vtl.rail-collapsed .vtl-twisty { display:none; }
.vtl.rail-collapsed .vtl-tier { padding-left:196px; }
.vtl.rail-collapsed .vtl-tl, .vtl.rail-collapsed .vtl-td, .vtl.rail-collapsed .vtl-cap, .vtl.rail-collapsed .vtl-chev { display:none; }
.vtl.rail-collapsed .vtl-grp + .vtl-grp { margin-top:6px; padding-top:6px; }
@ -507,9 +487,6 @@ const VTL_SHADOW_CSS = `
.vtl.rail-collapsed .vtl-collapse-ic { transform:rotate(180deg); }
.vtl.rail-collapsed .vtl-collapse-tx { display:inline; }
.vtl.rail-collapsed .vtl-tab { padding-left:10px; gap:11px; }
.vtl.rail-collapsed .vtl-tab[data-depth] { padding-left:calc(12px + var(--vtl-depth) * 16px); }
.vtl.rail-collapsed .vtl-tab[data-depth]::before { display:block; }
.vtl.rail-collapsed .vtl-twisty { display:inline-flex; }
.vtl.rail-collapsed .vtl-tab-text { display:flex; }
.vtl.rail-collapsed .vtl-tab-fallback { display:none; }
.vtl.rail-collapsed .vtl-tier { padding-left:8px; }
@ -550,7 +527,7 @@ export default defineComponent({
// tab (so their pane is rendered + element mounted); hostState/hostError track each pane's load.
// A row not in activatedKeys is never rendered — the lazy contract (no visit → no cost).
return {
navOpen: this.initialNavOpen, railCollapsed: this.initialRailCollapsed, _tabEls: {}, folderOverride: {}, branchOpen: {},
navOpen: this.initialNavOpen, railCollapsed: this.initialRailCollapsed, _tabEls: {}, folderOverride: {},
// `narrow` mirrors the (max-width:760px) media query as REACTIVE state, kept current by a resize
// listener. It lets us reconcile the two boolean state machines (navOpen = mobile drawer,
// railCollapsed = desktop slim rail) at the breakpoint and drive rail visibility correctly.
@ -573,9 +550,7 @@ export default defineComponent({
return this.items
},
// Flat ordered list of keys — drives keyboard nav.
// An ALIAS row (row.selects set) repeats another row — it is skipped here, so arrow keys walk
// each plugin once instead of stopping on every copy.
orderedKeys() { return this.allRows.filter(r => !r.selects).map(r => r.key) },
orderedKeys() { return this.allRows.map(r => r.key) },
// Rows that ask THIS layout to host a nested custom element (have an `element` tag).
elementRows() { return this.allRows.filter(r => r && r.element) },
// Element rows that have been activated at least once — only these get a rendered/mounted pane
@ -638,6 +613,9 @@ export default defineComponent({
this.navOpen = true
}
},
// Open the desktop rail if it is in its slim state (a #rail host calls this when its slim-strip
// icons are clicked: "show me that section" means the full rail). No-op when already open.
expandRail() { if (!this.narrow && this.railCollapsed) this.railCollapsed = false },
toggleRail() {
if (this.narrow) this.navOpen = !this.navOpen
else this.railCollapsed = !this.railCollapsed
@ -709,17 +687,7 @@ export default defineComponent({
const wasOpen = cur != null ? cur : (grp && grp.open === true)
this.folderOverride = { ...this.folderOverride, [key]: !wasOpen }
},
// The key a row selects: its own, or — for an ALIAS row — the canonical row it points at.
target(row) { return row.selects || row.key },
// BRANCHES — a row with `branch` folds the rows whose `ancestors` include it. Closed by default,
// so a deep composition tree costs one line until someone asks to see inside it.
isBranchOpen(id) { return !!this.branchOpen[id] },
toggleBranch(id) { this.branchOpen = { ...this.branchOpen, [id]: !this.branchOpen[id] } },
rowShown(row) { return !row.ancestors || row.ancestors.every(id => this.branchOpen[id]) },
pick(key, row) {
// Choosing a row that owns a branch also OPENS it (never closes it): you asked for that plugin,
// so show what it is made of. Only the chevron closes a branch.
if (row && row.branch && !this.branchOpen[row.branch]) this.toggleBranch(row.branch)
pick(key) {
this.$emit('update:active', key)
// On mobile, picking a tab closes the off-canvas drawer. On desktop navOpen must stay true (it gates
// the desktop rail's visibility via railShown), so we only touch it when narrow.
@ -847,13 +815,6 @@ export default defineComponent({
THIS RULE EXISTS TWICE (VTL_SHADOW_CSS and the scoped style block) and the two copies must stay
byte-identical — edit both or neither. */
.vtl-tab { display:flex; align-items:center; gap:11px; width:100%; min-height:48px; text-align:left; border:0; background:none; color:var(--vtl-muted); padding:8px 12px; cursor:pointer; border-radius:var(--vtl-radius,8px); font:inherit; margin:0; transition:background .13s ease,color .13s ease; }
.vtl-tab[data-depth] { padding-left:calc(12px + var(--vtl-depth) * 16px); }
.vtl-tab[data-depth]::before { content:''; flex:0 0 auto; align-self:stretch; width:0; margin-right:-3px; border-left:2px solid var(--vtl-accent); opacity:.45; }
.vtl-rowwrap { position:relative; }
.vtl-twisty { position:absolute; z-index:1; right:4px; top:50%; transform:translateY(-50%); width:28px; height:28px; display:inline-flex; align-items:center; justify-content:center; padding:0; border:0; border-radius:8px; background:none; color:var(--vtl-muted); cursor:pointer; }
.vtl-twisty:hover { background:var(--vtl-accented); color:#3b463d; }
.vtl-twisty:focus-visible { outline:2px solid var(--vtl-accent); outline-offset:-2px; }
.vtl-tab.has-branch { padding-right:36px; }
.vtl-tab:hover { background:var(--vtl-accented); color:#3b463d; }
.vtl-tab:focus-visible { outline:2px solid var(--vtl-accent); outline-offset:-2px; }
.vtl-tab.active { background:var(--vtl-accented); color:#3b463d; font-weight:500; }
@ -948,8 +909,6 @@ export default defineComponent({
.vtl.rail-collapsed .vtl-tab-text { display:none; }
.vtl.rail-collapsed .vtl-tab-emoji { font-size:19px; }
.vtl.rail-collapsed .vtl-tab-fallback { display:inline-flex; }
.vtl.rail-collapsed .vtl-tab[data-depth]::before { display:none; }
.vtl.rail-collapsed .vtl-twisty { display:none; }
.vtl.rail-collapsed .vtl-tier { padding-left:196px; }
.vtl.rail-collapsed .vtl-tl, .vtl.rail-collapsed .vtl-td, .vtl.rail-collapsed .vtl-cap, .vtl.rail-collapsed .vtl-chev { display:none; }
.vtl.rail-collapsed .vtl-grp + .vtl-grp { margin-top:6px; padding-top:6px; }
@ -974,9 +933,6 @@ export default defineComponent({
.vtl.rail-collapsed .vtl-collapse-ic { transform:rotate(180deg); }
.vtl.rail-collapsed .vtl-collapse-tx { display:inline; }
.vtl.rail-collapsed .vtl-tab { padding-left:10px; gap:11px; }
.vtl.rail-collapsed .vtl-tab[data-depth] { padding-left:calc(12px + var(--vtl-depth) * 16px); }
.vtl.rail-collapsed .vtl-tab[data-depth]::before { display:block; }
.vtl.rail-collapsed .vtl-twisty { display:inline-flex; }
.vtl.rail-collapsed .vtl-tab-text { display:flex; }
.vtl.rail-collapsed .vtl-tab-fallback { display:none; }
.vtl.rail-collapsed .vtl-tier { padding-left:8px; }

View file

@ -336,6 +336,22 @@ for (const [c, v] of Object.entries(codes)) {
// to be distinguishable from "nobody looked".
const SCANNED = 'ebible, arclight, door43, fcbh, scriptureearth, grn'
const NOT_SCANNED = 'api_bible, dbl, progress_bible'
// POOLS CONSIDERED AND REJECTED, with the measurement, so nobody spends another tick on
// them. Each was reachable and doctrinally fine — the reason is always coverage or the
// join key, never suspicion:
// - AudioTreasure 9 languages, every one major (Arabic, Cantonese, Hindi, Mandarin,
// Polish, Punjabi, Tamil, Urdu). Zero overlap with the gap.
// - Talking Bibles 177 languages, real audio Bibles, open robots.txt. They publish NO
// ISO code — the player keys languages by an internal numeric id — so a
// join would have to be by NAME, which this map refuses because names
// collide across unrelated languages. It did not matter in the end: of
// the 81 whose names match a language we carry, ZERO are in the gap, so
// even a perfect ISO join would add no coverage.
// - Davar Partners a ministry site, not a catalogue. No per-language page and therefore
// no URL a worker could be sent to.
// GRN was the opposite case and is worth the contrast: also name-shy at first glance, but
// their 5fish site resolves by ISO 639-3 directly, which is why it is in and these are not.
// SIGN LANGUAGES GET THEIR OWN PROVENANCE, because the generic list is actively
// misleading for them. A Deaf group is not served by eBible (text), Door43 (text and
// stories) or FCBH audio, so listing those as "checked" implies a search that was never

View file

@ -54,10 +54,15 @@
:title="heading"
nav-label="Plugin elements · by capability 5 → 1"
:header="true"
:groups="navGroups"
:active="activeKey"
@update:active="select">
<!-- #rail: the Explorer tree (src/StudioTree.vue) — capability sections, each composite folding
to show what it is made of. The layout keeps the rail frame (collapse, drawer, slim strip). -->
<template #rail="{ collapsed, expand, pick }">
<StudioTree :sections="treeSections" :active="activeKey" :collapsed="collapsed" :expand="expand" :pick="pick" />
</template>
<!-- #brand: the studio's logo mark (a plug/puzzle glyph — "plugin studio"), left of the title.
Self-drawn SVG, green accent to match the admin shell; no external asset (offline safe). -->
<template #brand>
@ -152,12 +157,15 @@
import { defineComponent, inject } from 'vue'
import { useShadowStyles } from '@map/composables/useShadowStyles.js'
import VerticalTabsLayout from '@map/layouts/VerticalTabsLayout.vue'
import StudioTree from './src/StudioTree.vue'
import { SWITCHER_LANGUAGES, loadLocale, saveLocale, getLanguage } from '@map/layouts/app-wrapper-material/languages.js'
// Only the studio's CONTENT-AREA styles remain here; the shell (top bar + rail + body) is now the
// shared VerticalTabsLayout (which self-injects its own CSS). Kept as an injected string so the studio
// content is styled even when this component is nested as a child element (see useShadowStyles).
const PS_SHADOW_CSS = `
:host { display:block; position:relative; height:var(--plugin-studio-height, 85vh); min-height:480px; }
:host([hidden]) { display:none; }
.ps-logo { display:inline-flex; align-items:center; }
.ps-logo svg { display:block; }
.ps-count { color:#6d766e; }
@ -247,6 +255,8 @@ const capFor = (t) => (PT[t] != null ? 5 - PT[t] : 1)
// Rail row icon per capability: LOCAL inline SVG names (app-wrapper-material/icons.js), not emoji.
// These were emoji until 2026-09-24, and on a box with no emoji font (measured: this one) every rail
// row led with an empty tofu box — the first thing a developer saw on every plugin.
// Short tier names for the rail's section headers (the full CAP_LABEL is their tooltip).
const CAP_SHORT = { 5: 'App shell', 4: 'Composite', 3: 'Multi-view', 2: 'Preset', 1: 'Single' }
const CAP_ICON = { 5: 'monitor', 4: 'tree', 3: 'layers', 2: 'grid', 1: 'embed' }
// MAP ELEMENTS. These own a live WebGL context, so they are the rows the studio DESTROYS on
@ -342,7 +352,7 @@ function whenTagDefined(tag, ms) {
export default defineComponent({
name: 'PluginStudio',
components: { VerticalTabsLayout },
components: { VerticalTabsLayout, StudioTree },
props: {
treeBase: { type: String, default: '' },
profileConfig: { type: [String, Object], default: '' },
@ -409,64 +419,48 @@ export default defineComponent({
.filter(c => byCap[c] && byCap[c].length)
.map(c => ({ cap: c, label: CAP_LABEL[c] || ('capability ' + c), rows: byCap[c] }))
},
// The GROUPED rail model the shared VerticalTabsLayout paints: ONE list, highest capability to
// lowest (Driver, 2026-09-24: "everything in studio organized by highest capability to lowest";
// "the composability in the capability sidebar vertical tabs … to understand the interoperability
// and the various elements that go into each plugin").
// The RAIL MODEL the Explorer tree (src/StudioTree.vue) paints: ONE list, highest capability to
// lowest (Driver, 2026-09-24: "everything in studio organized by highest capability to lowest",
// "the composability in the capability sidebar … to understand the interoperability and the
// various elements that go into each plugin", "it needs to be collapsible").
//
// CAP 5 → CAP 1 sections, collapsible, open by default. Every plugin has exactly ONE canonical
// row, in its own tier. Directly under a COMPOSITE's row sits its composition tree: every element
// nested into it, indented by depth, ordered by capability at each level. So reading down the
// rail never jumps back up the ladder — the tree lives with the plugin it explains.
// CAP 5 → CAP 1 sections. Every plugin has exactly ONE canonical node (depth 0) in its own tier,
// and a composite's parts hang under it (depth 1+), ordered by capability at each level, folded
// until opened.
//
// ★ HOW THE REPEATS STAY ONE IDENTITY. Every tree row is an ALIAS (VerticalTabsLayout `selects`):
// people-groups-grid under dashboard-plus-grid selects the SAME canonical row as its own CAP 2
// entry and highlights both, and arrow keys skip aliases. afe6abe removed the first rail folders
// because six independent nodes for one plugin left no single name to report it by; an alias has
// no separate identity, so nesting and the one-name rule coexist. The section count counts
// canonical plugins only.
navGroups() {
const esc = (s) => String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
// ★ HOW THE REPEATS STAY ONE IDENTITY. A part node carries `target` = the canonical row key, so
// people-groups-grid under dashboard-plus-grid opens the SAME pane as its own CAP 2 entry and
// highlights both. afe6abe removed the first rail folders because six independent nodes for one
// plugin left no single name to report it by; a node that only TARGETS the canonical row has no
// identity of its own, so nesting and the one-name rule coexist.
treeSections() {
const treeOf = new Map(this.compositionTrees.map(t => [t.nodes[0].selects, t]))
// ★ FOLDED BY DEFAULT (Driver, 2026-09-24: "it needs to be collapsible, otherwise it is too
// hard to know how to navigate so many nested plugins"). Every node with parts is a BRANCH with
// its own chevron, closed until opened — so the rail first reads as a plain list of plugins, and
// you drill into one composite, then one level of it, at a time. Picking a composite opens it.
// Branch ids are namespaced by the composite's row key: the same child (dashboard-plus-grid)
// folds independently under doxa-maps-spa and in its own CAP 4 entry.
const parts = (n) => n + (n === 1 ? ' part' : ' parts')
const rowsFor = (r) => {
const t = treeOf.get(r.key)
const bid = (id) => 'b:' + r.key + ':' + id
const out = [{
key: r.key, label: r.label, icon: r.icon,
branch: t ? bid(0) : undefined,
sub: '<code>&lt;' + esc(r.tag) + '&gt;</code>' + (t ? ' · ' + parts(t.nodes[0].kids) : '')
}]
if (t) {
t.nodes.slice(1).forEach((n) => out.push({
key: 'tree-' + r.key + '-' + n.id,
selects: n.selects || ('none:' + r.key),
depth: n.depth,
label: n.label,
badge: n.part ? 'part' : '',
branch: n.kids ? bid(n.id) : undefined,
ancestors: n.ancestors.map(bid),
sub: n.part ? 'no standalone page'
: 'CAP ' + n.cap + ' · <code>&lt;' + esc(n.tag) + '&gt;</code>' + (n.kids ? ' · ' + parts(n.kids) : '')
}))
return this.groups.map(g => {
const nodes = []
for (const r of g.rows) {
const t = treeOf.get(r.key)
nodes.push({
id: r.key, parent: '', depth: 0, label: r.label, desc: r.tag, icon: r.icon, target: r.key,
kids: t ? t.nodes[0].kids : 0, title: r.label + ' · <' + r.tag + '> · CAP ' + r.cap + ' ' + (CAP_LABEL[r.cap] || '')
})
if (!t) continue
const nid = (id) => (id === 0 ? r.key : r.key + '>' + id)
for (const n of t.nodes.slice(1)) {
nodes.push({
id: nid(n.id), parent: nid(n.ancestors[n.ancestors.length - 1]), depth: n.depth,
label: n.label, part: !!n.part, target: n.selects || '',
icon: n.part ? 'dots' : (CAP_ICON[n.cap] || 'embed'),
desc: n.part ? 'no standalone page' : 'cap ' + n.cap + ' · ' + n.tag,
kids: n.kids || 0,
title: n.part ? n.label + ' · part of ' + r.label + ', not published on its own'
: n.label + ' · <' + n.tag + '> · CAP ' + n.cap + ' ' + (CAP_LABEL[n.cap] || '')
})
}
}
return out
}
return this.groups.map(g => ({
key: 'cap-' + g.cap,
badge: 'CAP ' + g.cap,
label: g.label,
count: g.rows.length,
folder: true,
open: true,
rows: g.rows.flatMap(rowsFor)
}))
// Short section names: the header shares a 256px rail with its CAP badge and count, and the
// full "App shell · nested-mfe-spa" truncated to "APP SHELL · NEST…". The long name is the tooltip.
return { id: 'cap-' + g.cap, cap: g.cap, label: CAP_SHORT[g.cap] || g.label, title: g.label, icon: CAP_ICON[g.cap] || 'embed', nodes }
})
},
// ★ ONE TREE PER COMPOSITE, built ENTIRELY from the manifest's `composes` edges — no bundle names
// or child lists live in this file, so a new composite appears here with zero edits.
@ -797,6 +791,13 @@ export default defineComponent({
</script>
<style>
/* ★ THE HOST HAS A HEIGHT OF ITS OWN (same rule as doxa-maps-spa). A custom element is display:inline
by default and the shell inside it is absolutely positioned, so pasting the published embed snippet
into a page rendered a fully mounted studio 0px tall — measured 2026-09-24. A default, not a lock:
the page's own style="height:…" or --plugin-studio-height wins, which is how the leaf page fills
the viewport. Mirrored in PS_SHADOW_CSS above. */
:host { display: block; position: relative; height: var(--plugin-studio-height, 85vh); min-height: 480px; }
:host([hidden]) { display: none; }
/* The shell (top bar + rail + body) is the shared VerticalTabsLayout. Only the studio's own
content-area styles remain here; mirror of PS_SHADOW_CSS above. Keep the two in sync.
Reskinned to the dx-life admin tokens (neutral surfaces, green #92b195 accent) to match the shell. */

View file

@ -0,0 +1,373 @@
<!--
StudioTree.vue — the plugin studio's rail: an EXPLORER TREE, imitating VS Code's Explorer.
★ WHY IMITATE, NOT INVENT (Kingdom-agent directive, 2026-09-24): the studio nests every plugin, and
composites nest composites — a hierarchy too deep and too repetitive to hand-roll as tabs. The
leader for exactly this problem is the VS Code Explorer (researched against VS Code's views UX
guidelines, its tree settings, Storybook's sidebar and the WAI-ARIA APG Tree View pattern; notes in
_qa-cycle/studio-organize/RESEARCH-STUDIO-VERTICAL-TABS-IMITATE-THE-LEADER.md). What is copied:
- collapsible SECTIONS with sticky uppercase headers (VS Code views) → CAP 5 … CAP 1
- twisty on the LEFT, space reserved on leaves so labels line up
- one-line rows: label + a muted DESCRIPTION → the element tag
- indent guides, always on (renderIndentGuides "always")
- a FILTER box on top (VS Code tree find, Storybook search): matches plus their parents
- one view action only — Collapse all ("be careful not to add too many actions")
- AUTO-REVEAL: opening a plugin by click, URL hash or restored tab expands and scrolls to it
- the ACTIVITY BAR when the rail is slim: one icon per section, click → rail opens at it
- APG keyboard: ↓/↑ move, → open / first child, ← close / parent, Home/End, Enter activates,
type a letter to jump
Composites start FOLDED (Driver: "too hard to navigate so many nested plugins"); the open/closed
state is remembered per page, as VS Code remembers expansion.
★ ONE TREE, SECTIONS AS LEVEL-1 NODES. VS Code's section headers are separate pane buttons; here they
are treeitems at aria-level 1 so arrow keys walk the whole rail in one pass instead of stopping at
every section border. Plugins are level 2+, a composite's parts one level deeper each.
★ A REPEATED CHILD IS NOT A SECOND PLUGIN. A part row (people-groups-grid under dashboard-plus-grid)
TARGETS the one canonical row: choosing it opens the same pane and highlights every copy. That is
what keeps "name the one element that is broken" possible with repeats on screen.
Rendered as a CHILD of the <plugin-studio> custom element, inside its shadow root, through
VerticalTabsLayout's #rail slot. Child-SFC <style> is dropped by Vue in a custom element, so the CSS
is injected with useShadowStyles; the <style> block below is the same CSS for dev — keep in sync.
-->
<template>
<!-- SLIM RAIL = the activity bar: one icon per capability section in the strip that stays visible. -->
<div v-if="collapsed" class="st-activity">
<button v-for="s in sections" :key="s.id" type="button" class="st-act"
:class="{ active: s.id === activeSectionId }"
:aria-label="'Open CAP ' + s.cap + ', ' + s.label"
@click="openSectionFromActivity(s.id)">
<span class="st-act-ic" aria-hidden="true" v-html="svg(s.icon)"></span>
<span class="st-act-n" aria-hidden="true">{{ s.cap }}</span>
</button>
</div>
<div v-else class="st">
<div class="st-bar">
<label class="st-find">
<span class="st-find-ic" aria-hidden="true" v-html="svg('search')"></span>
<input ref="find" type="search" class="st-find-in" v-model="query" placeholder="Filter plugins"
aria-label="Filter plugins" @keydown="onFindKey" />
</label>
<button type="button" class="st-tool" aria-label="Collapse all" title="Collapse all" @click="collapseAll">
<span aria-hidden="true" v-html="collapseAllSvg"></span>
</button>
</div>
<div class="st-tree" role="tree" aria-label="Plugins by capability" @keydown="onTreeKey">
<div v-for="item in visible" :key="item.id"
:ref="el => setRow(item.id, el)"
role="treeitem"
:aria-level="item.level"
:aria-expanded="item.expandable ? String(isOpen(item)) : null"
:aria-selected="item.section ? null : String(item.target === active)"
:tabindex="item.id === focusId ? 0 : -1"
:class="rowClass(item)"
:style="{ '--st-depth': item.depth }"
:title="item.title || null"
@click="onRowClick(item)"
@focus="focusId = item.id">
<template v-if="item.section">
<span class="st-twisty" aria-hidden="true" v-html="chev"></span>
<span class="st-cap">CAP {{ item.cap }}</span>
<span class="st-sec-label">{{ item.label }}</span>
<span class="st-count">{{ item.count }}</span>
</template>
<template v-else>
<span v-for="g in item.depth" :key="g" class="st-guide" :style="{ '--st-g': g - 1 }" aria-hidden="true"></span>
<span v-if="item.expandable" class="st-twisty" aria-hidden="true" v-html="chev" @click.stop="toggle(item)"></span>
<span v-else class="st-twisty-sp" aria-hidden="true"></span>
<span class="st-ic" aria-hidden="true" v-html="svg(item.icon)"></span>
<span class="st-label">{{ item.label }}</span>
<span class="st-part" v-if="item.part">part</span>
<span class="st-desc">{{ item.desc }}</span>
<span class="st-count" v-if="item.expandable">{{ item.kids }}</span>
</template>
</div>
<p class="st-empty" v-if="query && !visible.length">No plugin matches “{{ query }}”.</p>
</div>
</div>
</template>
<script>
import { defineComponent } from 'vue'
import { useShadowStyles } from '@map/composables/useShadowStyles.js'
import { icon as localIcon } from '@map/layouts/app-wrapper-material/icons.js'
const ST_CSS = '' +
'.st { display:flex; flex-direction:column; margin:0 -12px; }' +
'.st-bar { position:sticky; top:0; z-index:3; display:flex; align-items:center; gap:6px; padding:8px 12px 8px; background:#ffffff; }' +
'.st-find { flex:1 1 auto; min-width:0; display:flex; align-items:center; gap:6px; height:30px; padding:0 8px; border:1px solid #d1d4cd; border-radius:8px; background:#f3f3f1; color:#6d766e; }' +
'.st-find:focus-within { border-color:#92b195; background:#ffffff; }' +
'.st-find-ic { display:inline-flex; } .st-find-ic svg { width:14px; height:14px; }' +
'.st-find-in { flex:1 1 auto; min-width:0; border:0; outline:0; background:none; font:inherit; font-size:13px; color:#3b463d; }' +
'.st-tool { flex:0 0 auto; width:30px; height:30px; display:inline-flex; align-items:center; justify-content:center; padding:0; border:0; border-radius:8px; background:none; color:#6d766e; cursor:pointer; }' +
'.st-tool:hover { background:#f3f3f1; color:#3b463d; } .st-tool:focus-visible { outline:2px solid #92b195; outline-offset:-2px; }' +
'.st-tool svg { width:16px; height:16px; }' +
'.st-tree { display:flex; flex-direction:column; padding:0 0 8px; outline:0; }' +
'.st-row { position:relative; display:flex; align-items:center; gap:6px; height:28px; box-sizing:border-box; padding:0 10px 0 calc(8px + var(--st-depth) * 14px); font-size:13px; color:#3b463d; cursor:pointer; white-space:nowrap; user-select:none; outline:0; }' +
'.st-row:hover { background:#f3f3f1; }' +
'.st-row:focus-visible { box-shadow:inset 0 0 0 1px #92b195; }' +
'.st-row.active { background:rgba(146,177,149,.24); }' +
'.st-row.active::after { content:""; position:absolute; left:0; top:4px; bottom:4px; width:3px; border-radius:0 3px 3px 0; background:#92b195; }' +
'.st-row.alias.active { background:rgba(146,177,149,.12); }' +
'.st-sec { position:sticky; top:46px; z-index:2; height:30px; margin-top:6px; background:#ffffff; font-size:11px; font-weight:600; letter-spacing:.06em; text-transform:uppercase; color:#6d766e; border-top:1px solid #eceee9; }' +
'.st-sec:first-child { margin-top:0; border-top:0; }' +
'.st-sec:hover { background:#ffffff; color:#3b463d; }' +
'.st-cap { flex:0 0 auto; font:700 10px/1 system-ui,sans-serif; letter-spacing:.3px; padding:3px 6px; border-radius:999px; background:#92b195; color:#1d2a1f; }' +
'.st-sec-label { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; }' +
'.st-twisty, .st-twisty-sp { flex:0 0 16px; width:16px; height:16px; display:inline-flex; align-items:center; justify-content:center; color:#6d766e; }' +
'.st-twisty svg { width:14px; height:14px; transition:transform .12s ease; }' +
'.st-row[aria-expanded="true"] > .st-twisty svg { transform:rotate(90deg); }' +
'.st-ic { flex:0 0 16px; display:inline-flex; color:#6d766e; } .st-ic svg { width:16px; height:16px; }' +
'.st-row.active .st-ic { color:#5f8a63; }' +
'.st-label { flex:0 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; }' +
// flex-basis 0 on the DESCRIPTION: it only gets what the name leaves over, so the name is never
// ellipsised to make room for the tag (VS Code truncates the description first, too).
'.st-desc { flex:1 1 0; min-width:0; overflow:hidden; text-overflow:ellipsis; font:11px/1 ui-monospace,Menlo,monospace; color:#8a928b; }' +
'.st-part { flex:0 0 auto; font:600 9px/1 system-ui,sans-serif; text-transform:uppercase; letter-spacing:.3px; padding:2px 4px; border:1px solid #92b195; border-radius:4px; color:#5f8a63; }' +
'.st-count { flex:0 0 auto; margin-left:auto; min-width:18px; text-align:right; font-size:11px; font-weight:500; letter-spacing:0; color:#6d766e; }' +
'.st-guide { position:absolute; top:0; bottom:0; left:calc(15px + var(--st-g) * 14px); border-left:1px solid #dfe2db; pointer-events:none; }' +
'.st-row:hover > .st-guide { border-left-color:#c4c8c0; }' +
'.st-empty { margin:12px; font-size:13px; color:#6d766e; }' +
'.st-activity { display:flex; flex-direction:column; gap:4px; }' +
'.st-act { position:relative; display:flex; align-items:center; justify-content:center; width:48px; height:44px; margin-left:172px; padding:0; border:0; border-radius:10px; background:none; color:#6d766e; cursor:pointer; }' +
'.st-act:hover, .st-act.active { background:#f3f3f1; color:#3b463d; }' +
'.st-act:focus-visible { outline:2px solid #92b195; outline-offset:-2px; }' +
'.st-act-ic { display:inline-flex; } .st-act-ic svg { width:20px; height:20px; }' +
'.st-act-n { position:absolute; right:6px; bottom:5px; font:700 9px/1 system-ui,sans-serif; color:#5f8a63; }'
const COLLAPSE_ALL = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="3"/><line x1="8.5" y1="12" x2="15.5" y2="12"/></svg>'
const CHEV = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 6 15 12 9 18"/></svg>'
export default defineComponent({
name: 'StudioTree',
props: {
// [{ id, cap, label, icon, nodes: [{ id, parent, depth, label, desc, icon, target, kids, part, title }] }]
// nodes are in display order (a parent before its children), depth 0 = a plugin in its section.
sections: { type: Array, default: () => [] },
active: { type: String, default: '' },
collapsed: { type: Boolean, default: false },
pick: { type: Function, default: null },
expand: { type: Function, default: null }
},
emits: ['select'],
setup() { useShadowStyles(ST_CSS, 'studio-tree') },
data() {
const saved = this.readState()
return {
query: '',
focusId: '',
// Sections default OPEN, nodes default CLOSED; only deviations are stored.
secClosed: saved.sec || {},
nodeOpen: saved.node || {},
chev: CHEV,
collapseAllSvg: COLLAPSE_ALL
}
},
computed: {
byId() {
const m = new Map()
for (const s of this.sections) for (const n of s.nodes) m.set(n.id, n)
return m
},
q() { return this.query.trim().toLowerCase() },
// Filter mode (VS Code tree find): a node stays if it matches, or if something under it matches —
// so a match is always shown WITH the path to it. Everything is expanded while filtering.
kept() {
if (!this.q) return null
const keep = new Set()
for (const s of this.sections) {
for (const n of s.nodes) {
const hit = n.label.toLowerCase().includes(this.q) || String(n.desc || '').toLowerCase().includes(this.q)
if (!hit) continue
keep.add(n.id)
let p = n.parent
while (p) { keep.add(p); p = this.byId.get(p) && this.byId.get(p).parent }
}
}
return keep
},
// The flat, ordered list of rows actually on screen — what arrow keys walk.
visible() {
const out = []
for (const s of this.sections) {
const nodes = this.kept ? s.nodes.filter(n => this.kept.has(n.id)) : s.nodes
if (this.kept && !nodes.length) continue
out.push({
id: 'sec:' + s.id, section: true, sectionId: s.id, level: 1, depth: 0, expandable: true,
cap: s.cap, label: s.label, title: s.title || '', count: s.nodes.filter(n => n.depth === 0).length
})
if (!this.kept && this.secClosed[s.id]) continue
for (const n of nodes) {
if (!this.kept && !this.ancestorsOpen(n)) continue
out.push({ ...n, sectionId: s.id, level: n.depth + 2, expandable: !!n.kids, alias: n.depth > 0 })
}
}
return out
},
activeSectionId() {
for (const s of this.sections) if (s.nodes.some(n => n.depth === 0 && n.target === this.active)) return s.id
return ''
}
},
watch: {
// AUTO-REVEAL (explorer.autoReveal): whenever the active plugin changes — a click, a URL hash, a
// restored last tab — make its canonical row visible and scroll it into view.
active: { handler() { this.reveal() }, immediate: true },
sections() { this.reveal() },
query(v) { if (v && this.visible.length) this.focusId = this.visible[0].id }
},
methods: {
svg(name) { return name ? localIcon(name) : '' },
storageKey() { return 'dx-studio-tree:' + ((typeof location !== 'undefined' && location.pathname) || '') },
// Guarded: storage THROWS in private mode / under policy, and a throw here would blank the studio.
readState() { try { return JSON.parse(window.localStorage.getItem(this.storageKey()) || '{}') || {} } catch (_) { return {} } },
saveState() { try { window.localStorage.setItem(this.storageKey(), JSON.stringify({ sec: this.secClosed, node: this.nodeOpen })) } catch (_) { /* storage unavailable */ } },
ancestorsOpen(n) {
let p = n.parent
while (p) { if (!this.nodeOpen[p]) return false; p = this.byId.get(p) && this.byId.get(p).parent }
return true
},
isOpen(item) {
if (this.kept) return true
return item.section ? !this.secClosed[item.sectionId] : !!this.nodeOpen[item.id]
},
setOpen(item, open) {
if (item.section) this.secClosed = { ...this.secClosed, [item.sectionId]: !open }
else this.nodeOpen = { ...this.nodeOpen, [item.id]: open }
this.saveState()
},
toggle(item) { if (item.expandable) this.setOpen(item, !this.isOpen(item)) },
collapseAll() {
this.nodeOpen = {}
this.saveState()
const a = this.visible.find(i => !i.section && i.target === this.active && i.depth === 0)
if (a) this.focusId = a.id
},
rowClass(item) {
return {
'st-row': true,
'st-sec': !!item.section,
active: !item.section && item.target === this.active,
alias: !!item.alias
}
},
setRow(id, el) { if (!this._rows) this._rows = {}; if (el) this._rows[id] = el; else delete this._rows[id] },
// Choosing a plugin opens it in the pane (through the layout's pick, which also closes the mobile
// drawer) and unfolds its parts — you asked for that plugin, so show what it is made of. Only the
// twisty or ← folds. A part with no page of its own opens the composite it lives in.
activate(item) {
if (item.section) { this.toggle(item); return }
if (item.expandable && !this.isOpen(item)) this.setOpen(item, true)
if (!item.target) return
if (this.pick) this.pick(item.target); else this.$emit('select', item.target)
},
onRowClick(item) { this.focusId = item.id; this.activate(item) },
focusRow(id) {
if (!id) return
this.focusId = id
this.$nextTick(() => { const el = this._rows && this._rows[id]; if (el) { el.focus({ preventScroll: true }); el.scrollIntoView({ block: 'nearest' }) } })
},
// APG Tree View keyboard model.
onTreeKey(e) {
const list = this.visible
if (!list.length) return
const i = Math.max(0, list.findIndex(x => x.id === this.focusId))
const cur = list[i]
const parentRowId = (it) => it.section ? '' : (it.parent || ('sec:' + it.sectionId))
switch (e.key) {
case 'ArrowDown': this.focusRow((list[i + 1] || cur).id); break
case 'ArrowUp': this.focusRow((list[i - 1] || cur).id); break
case 'Home': this.focusRow(list[0].id); break
case 'End': this.focusRow(list[list.length - 1].id); break
case 'ArrowRight':
if (!cur.expandable) break
if (!this.isOpen(cur)) this.setOpen(cur, true)
else if (list[i + 1] && list[i + 1].level > cur.level) this.focusRow(list[i + 1].id)
break
case 'ArrowLeft':
if (cur.expandable && this.isOpen(cur) && !this.kept) this.setOpen(cur, false)
else this.focusRow(parentRowId(cur))
break
case 'Enter': case ' ': this.activate(cur); break
default:
// Type-ahead: jump to the next row whose name starts with the typed character.
if (e.key.length === 1 && /\S/.test(e.key) && !e.ctrlKey && !e.metaKey && !e.altKey) {
const ch = e.key.toLowerCase()
const order = list.slice(i + 1).concat(list.slice(0, i + 1))
const hit = order.find(x => String(x.label).toLowerCase().startsWith(ch))
if (hit) this.focusRow(hit.id)
break
}
return
}
e.preventDefault()
},
onFindKey(e) {
if (e.key === 'Escape') { this.query = ''; e.preventDefault() }
else if (e.key === 'ArrowDown' && this.visible.length) { this.focusRow(this.visible[0].id); e.preventDefault() }
},
reveal() {
if (!this.active || !this.sections.length) return
let row = null
for (const s of this.sections) {
row = s.nodes.find(n => n.depth === 0 && n.target === this.active)
if (row) { if (this.secClosed[s.id]) { this.secClosed = { ...this.secClosed, [s.id]: false }; this.saveState() } break }
}
if (!row) return
if (!this.focusId || !this.byId.has(this.focusId) || this.byId.get(this.focusId).target !== this.active) this.focusId = row.id
this.$nextTick(() => { const el = this._rows && this._rows[row.id]; if (el && el.scrollIntoView) el.scrollIntoView({ block: 'nearest' }) })
},
openSectionFromActivity(id) {
if (this.secClosed[id]) { this.secClosed = { ...this.secClosed, [id]: false }; this.saveState() }
if (this.expand) this.expand()
this.$nextTick(() => this.focusRow('sec:' + id))
}
}
})
</script>
<style>
/* Mirror of ST_CSS above (dev / light-DOM readability). Keep the two in sync. */
.st { display:flex; flex-direction:column; margin:0 -12px; }
.st-bar { position:sticky; top:0; z-index:3; display:flex; align-items:center; gap:6px; padding:8px 12px 8px; background:#ffffff; }
.st-find { flex:1 1 auto; min-width:0; display:flex; align-items:center; gap:6px; height:30px; padding:0 8px; border:1px solid #d1d4cd; border-radius:8px; background:#f3f3f1; color:#6d766e; }
.st-find:focus-within { border-color:#92b195; background:#ffffff; }
.st-find-ic { display:inline-flex; } .st-find-ic svg { width:14px; height:14px; }
.st-find-in { flex:1 1 auto; min-width:0; border:0; outline:0; background:none; font:inherit; font-size:13px; color:#3b463d; }
.st-tool { flex:0 0 auto; width:30px; height:30px; display:inline-flex; align-items:center; justify-content:center; padding:0; border:0; border-radius:8px; background:none; color:#6d766e; cursor:pointer; }
.st-tool:hover { background:#f3f3f1; color:#3b463d; } .st-tool:focus-visible { outline:2px solid #92b195; outline-offset:-2px; }
.st-tool svg { width:16px; height:16px; }
.st-tree { display:flex; flex-direction:column; padding:0 0 8px; outline:0; }
.st-row { position:relative; display:flex; align-items:center; gap:6px; height:28px; box-sizing:border-box; padding:0 10px 0 calc(8px + var(--st-depth) * 14px); font-size:13px; color:#3b463d; cursor:pointer; white-space:nowrap; user-select:none; outline:0; }
.st-row:hover { background:#f3f3f1; }
.st-row:focus-visible { box-shadow:inset 0 0 0 1px #92b195; }
.st-row.active { background:rgba(146,177,149,.24); }
.st-row.active::after { content:""; position:absolute; left:0; top:4px; bottom:4px; width:3px; border-radius:0 3px 3px 0; background:#92b195; }
.st-row.alias.active { background:rgba(146,177,149,.12); }
.st-sec { position:sticky; top:46px; z-index:2; height:30px; margin-top:6px; background:#ffffff; font-size:11px; font-weight:600; letter-spacing:.06em; text-transform:uppercase; color:#6d766e; border-top:1px solid #eceee9; }
.st-sec:first-child { margin-top:0; border-top:0; }
.st-sec:hover { background:#ffffff; color:#3b463d; }
.st-cap { flex:0 0 auto; font:700 10px/1 system-ui,sans-serif; letter-spacing:.3px; padding:3px 6px; border-radius:999px; background:#92b195; color:#1d2a1f; }
.st-sec-label { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; }
.st-twisty, .st-twisty-sp { flex:0 0 16px; width:16px; height:16px; display:inline-flex; align-items:center; justify-content:center; color:#6d766e; }
.st-twisty svg { width:14px; height:14px; transition:transform .12s ease; }
.st-row[aria-expanded="true"] > .st-twisty svg { transform:rotate(90deg); }
.st-ic { flex:0 0 16px; display:inline-flex; color:#6d766e; } .st-ic svg { width:16px; height:16px; }
.st-row.active .st-ic { color:#5f8a63; }
.st-label { flex:0 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; }
.st-desc { flex:1 1 0; min-width:0; overflow:hidden; text-overflow:ellipsis; font:11px/1 ui-monospace,Menlo,monospace; color:#8a928b; }
.st-part { flex:0 0 auto; font:600 9px/1 system-ui,sans-serif; text-transform:uppercase; letter-spacing:.3px; padding:2px 4px; border:1px solid #92b195; border-radius:4px; color:#5f8a63; }
.st-count { flex:0 0 auto; margin-left:auto; min-width:18px; text-align:right; font-size:11px; font-weight:500; letter-spacing:0; color:#6d766e; }
.st-guide { position:absolute; top:0; bottom:0; left:calc(15px + var(--st-g) * 14px); border-left:1px solid #dfe2db; pointer-events:none; }
.st-row:hover > .st-guide { border-left-color:#c4c8c0; }
.st-empty { margin:12px; font-size:13px; color:#6d766e; }
.st-activity { display:flex; flex-direction:column; gap:4px; }
.st-act { position:relative; display:flex; align-items:center; justify-content:center; width:48px; height:44px; margin-left:172px; padding:0; border:0; border-radius:10px; background:none; color:#6d766e; cursor:pointer; }
.st-act:hover, .st-act.active { background:#f3f3f1; color:#3b463d; }
.st-act:focus-visible { outline:2px solid #92b195; outline-offset:-2px; }
.st-act-ic { display:inline-flex; } .st-act-ic svg { width:20px; height:20px; }
.st-act-n { position:absolute; right:6px; bottom:5px; font:700 9px/1 system-ui,sans-serif; color:#5f8a63; }
</style>

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 <all-people-groups> custom element. -->
<script src="../all-people-groups.js" integrity="sha384-i2Qv1FkHiMWhxFsOKGhfP4qSGTke/57SE1nrvk7ZV0lxfA5O92Ez6ZWf2dRbO88U" crossorigin="anonymous"></script>
<script src="../all-people-groups.js" integrity="sha384-j2rnWAW0lVUG05WREAfkQZo0jtreSIebwV3nHCbUcXhAGzMfTESwA1PqTYy5M3QS" 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

@ -26,7 +26,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <cities-internet-map> custom element. -->
<script src="../cities-internet.js" integrity="sha384-cLy6LqX5n+KGC7VdympqtscDcHqEKchYgMh4GJbHO5Z6X9nYcD3IT7/3Crq/BGEE" crossorigin="anonymous"></script>
<script src="../cities-internet.js" integrity="sha384-UbT6S/JosIHXCB2ubdjB/4MisiPFz3cg7Qm+Zg3GDqSELJu6FK8bp+3bIErXkqMb" 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 <cities-internet-map> custom element. -->
<script src="../cities-internet.js" integrity="sha384-cLy6LqX5n+KGC7VdympqtscDcHqEKchYgMh4GJbHO5Z6X9nYcD3IT7/3Crq/BGEE" crossorigin="anonymous"></script>
<script src="../cities-internet.js" integrity="sha384-UbT6S/JosIHXCB2ubdjB/4MisiPFz3cg7Qm+Zg3GDqSELJu6FK8bp+3bIErXkqMb" 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 <cities-internet-map> custom element. -->
<script src="../cities-internet.js" integrity="sha384-cLy6LqX5n+KGC7VdympqtscDcHqEKchYgMh4GJbHO5Z6X9nYcD3IT7/3Crq/BGEE" crossorigin="anonymous"></script>
<script src="../cities-internet.js" integrity="sha384-UbT6S/JosIHXCB2ubdjB/4MisiPFz3cg7Qm+Zg3GDqSELJu6FK8bp+3bIErXkqMb" 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 <demo-map> custom element. -->
<script src="../demo-map.js" integrity="sha384-VGZMQWOWVouP4IkUTsZ/ElUORaUYu9+wJSlbs/8r6Lc6EU8VvLYHhmTOGCJv2Y6H" crossorigin="anonymous"></script>
<script src="../demo-map.js" integrity="sha384-1c8yrJm6kSIEfED30ZHPqjEr3oKC2cScuBz6jFh1UfcF6cHXeyYYiLCdh+qMxoaX" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -27,7 +27,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <demo-map> custom element. -->
<script src="../../demo-map.js" integrity="sha384-VGZMQWOWVouP4IkUTsZ/ElUORaUYu9+wJSlbs/8r6Lc6EU8VvLYHhmTOGCJv2Y6H" crossorigin="anonymous"></script>
<script src="../../demo-map.js" integrity="sha384-1c8yrJm6kSIEfED30ZHPqjEr3oKC2cScuBz6jFh1UfcF6cHXeyYYiLCdh+qMxoaX" 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

@ -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-Cmdw0USq0X4/+OshXBayXKy4U5ZBD0bR8YR/yaR/CZ76CYEMpb4bymgfug+5AGyU" crossorigin="anonymous"></script>
<script src="../../doxa-maps-spa.js" integrity="sha384-j+2dQDsN+9KytIjgxVIRzcpXECccCuZhRT+CXc5B89vbo1IhaB8ogkO2Dc6KM1oy" 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-Cmdw0USq0X4/+OshXBayXKy4U5ZBD0bR8YR/yaR/CZ76CYEMpb4bymgfug+5AGyU" crossorigin="anonymous"></script>
<script src="../doxa-maps-spa.js" integrity="sha384-j+2dQDsN+9KytIjgxVIRzcpXECccCuZhRT+CXc5B89vbo1IhaB8ogkO2Dc6KM1oy" 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-research-map> custom element. -->
<script src="../doxa-research-map.js" integrity="sha384-E7/WP0pDnwiG6noaZZI3Sx92Cg3JwluCqYOA5eU+x+UARKkliojcJBk3Yx6jua7a" crossorigin="anonymous"></script>
<script src="../doxa-research-map.js" integrity="sha384-5E85ld1dX83U/sZ0feFMIW0Z+OQ6ZWcxirGRQyNeWegDShbqld62C5FoZbMwpP0m" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -27,7 +27,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <doxa-research-map> custom element. -->
<script src="../../doxa-research-map.js" integrity="sha384-E7/WP0pDnwiG6noaZZI3Sx92Cg3JwluCqYOA5eU+x+UARKkliojcJBk3Yx6jua7a" crossorigin="anonymous"></script>
<script src="../../doxa-research-map.js" integrity="sha384-5E85ld1dX83U/sZ0feFMIW0Z+OQ6ZWcxirGRQyNeWegDShbqld62C5FoZbMwpP0m" 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-r/K0P50Wt1Qz2KkJ+fVPurKtAr68GqWKREiLb8DYZ2L0VmOQUE9FwZhrDMZzYscr" crossorigin="anonymous"></script>
<script src="../../doxa-simple-map.js" integrity="sha384-rTSm6VQFqDXUXdPPFwhEFFblDLC4oFuoJ2Wtw8+iv/qAYC1ciiZlRSOsf+Ur4v6G" 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-r/K0P50Wt1Qz2KkJ+fVPurKtAr68GqWKREiLb8DYZ2L0VmOQUE9FwZhrDMZzYscr" crossorigin="anonymous"></script>
<script src="../../doxa-simple-map.js" integrity="sha384-rTSm6VQFqDXUXdPPFwhEFFblDLC4oFuoJ2Wtw8+iv/qAYC1ciiZlRSOsf+Ur4v6G" 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-r/K0P50Wt1Qz2KkJ+fVPurKtAr68GqWKREiLb8DYZ2L0VmOQUE9FwZhrDMZzYscr" crossorigin="anonymous"></script>
<script src="../doxa-simple-map.js" integrity="sha384-rTSm6VQFqDXUXdPPFwhEFFblDLC4oFuoJ2Wtw8+iv/qAYC1ciiZlRSOsf+Ur4v6G" 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-r/K0P50Wt1Qz2KkJ+fVPurKtAr68GqWKREiLb8DYZ2L0VmOQUE9FwZhrDMZzYscr" crossorigin="anonymous"></script>
<script src="../../doxa-simple-map.js" integrity="sha384-rTSm6VQFqDXUXdPPFwhEFFblDLC4oFuoJ2Wtw8+iv/qAYC1ciiZlRSOsf+Ur4v6G" 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 <gospel-resources-map> custom element. -->
<script src="../gospel-resources.js" integrity="sha384-VSJBa0rj+G/h4+0MOKOTCyB726MtzIp4EahuDM0P8rPU+27JIRcy7g8ILkiIZa7c" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-tGmkizI0wLvGvK/ymOr0w5iXns+os1816InhYBq+759yZd9GZCzYLIgIGTmItyXL" 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 <gospel-resources-map> custom element. -->
<script src="../gospel-resources.js" integrity="sha384-VSJBa0rj+G/h4+0MOKOTCyB726MtzIp4EahuDM0P8rPU+27JIRcy7g8ILkiIZa7c" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-tGmkizI0wLvGvK/ymOr0w5iXns+os1816InhYBq+759yZd9GZCzYLIgIGTmItyXL" 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 <gospel-resources-map> custom element. -->
<script src="../gospel-resources.js" integrity="sha384-VSJBa0rj+G/h4+0MOKOTCyB726MtzIp4EahuDM0P8rPU+27JIRcy7g8ILkiIZa7c" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-tGmkizI0wLvGvK/ymOr0w5iXns+os1816InhYBq+759yZd9GZCzYLIgIGTmItyXL" 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

@ -26,7 +26,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <gospel-resources-map> custom element. -->
<script src="../gospel-resources.js" integrity="sha384-VSJBa0rj+G/h4+0MOKOTCyB726MtzIp4EahuDM0P8rPU+27JIRcy7g8ILkiIZa7c" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-tGmkizI0wLvGvK/ymOr0w5iXns+os1816InhYBq+759yZd9GZCzYLIgIGTmItyXL" 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 <gospel-resources-map> custom element. -->
<script src="../gospel-resources.js" integrity="sha384-VSJBa0rj+G/h4+0MOKOTCyB726MtzIp4EahuDM0P8rPU+27JIRcy7g8ILkiIZa7c" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-tGmkizI0wLvGvK/ymOr0w5iXns+os1816InhYBq+759yZd9GZCzYLIgIGTmItyXL" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -13,7 +13,7 @@
"bundle": "all-people-groups",
"scriptUrl": "./all-people-groups/all-people-groups.js",
"output": "doxa-maps/all-people-groups/all-people-groups.js",
"integrity": "sha384-i2Qv1FkHiMWhxFsOKGhfP4qSGTke/57SE1nrvk7ZV0lxfA5O92Ez6ZWf2dRbO88U",
"integrity": "sha384-j2rnWAW0lVUG05WREAfkQZo0jtreSIebwV3nHCbUcXhAGzMfTESwA1PqTYy5M3QS",
"elements": [
"all-people-groups"
],
@ -29,7 +29,7 @@
"scriptUrl": "./all-people-groups/all-people-groups.js",
"embed": {
"iframe": "<iframe src=\"./all-people-groups/all-people-groups/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=\"./all-people-groups/all-people-groups.js\" integrity=\"sha384-i2Qv1FkHiMWhxFsOKGhfP4qSGTke/57SE1nrvk7ZV0lxfA5O92Ez6ZWf2dRbO88U\" crossorigin=\"anonymous\"></script>\n<all-people-groups profile-config='{\"profile\":\"all-people-groups\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></all-people-groups>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./all-people-groups/all-people-groups.js\" integrity=\"sha384-j2rnWAW0lVUG05WREAfkQZo0jtreSIebwV3nHCbUcXhAGzMfTESwA1PqTYy5M3QS\" crossorigin=\"anonymous\"></script>\n<all-people-groups profile-config='{\"profile\":\"all-people-groups\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></all-people-groups>"
},
"bundle": "all-people-groups"
}
@ -105,7 +105,7 @@
"bundle": "cities-internet",
"scriptUrl": "./cities-internet/cities-internet.js",
"output": "doxa-maps/cities-internet/cities-internet.js",
"integrity": "sha384-cLy6LqX5n+KGC7VdympqtscDcHqEKchYgMh4GJbHO5Z6X9nYcD3IT7/3Crq/BGEE",
"integrity": "sha384-UbT6S/JosIHXCB2ubdjB/4MisiPFz3cg7Qm+Zg3GDqSELJu6FK8bp+3bIErXkqMb",
"elements": [
"cities-internet-map"
],
@ -121,7 +121,7 @@
"scriptUrl": "./cities-internet/cities-internet.js",
"embed": {
"iframe": "<iframe src=\"./cities-internet/connectivity-list/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=\"./cities-internet/cities-internet.js\" integrity=\"sha384-cLy6LqX5n+KGC7VdympqtscDcHqEKchYgMh4GJbHO5Z6X9nYcD3IT7/3Crq/BGEE\" crossorigin=\"anonymous\"></script>\n<cities-internet-map profile-config='{\"profile\":\"connectivity-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-UbT6S/JosIHXCB2ubdjB/4MisiPFz3cg7Qm+Zg3GDqSELJu6FK8bp+3bIErXkqMb\" crossorigin=\"anonymous\"></script>\n<cities-internet-map profile-config='{\"profile\":\"connectivity-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
}
},
{
@ -135,7 +135,7 @@
"scriptUrl": "./cities-internet/cities-internet.js",
"embed": {
"iframe": "<iframe src=\"./cities-internet/connectivity-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=\"./cities-internet/cities-internet.js\" integrity=\"sha384-cLy6LqX5n+KGC7VdympqtscDcHqEKchYgMh4GJbHO5Z6X9nYcD3IT7/3Crq/BGEE\" crossorigin=\"anonymous\"></script>\n<cities-internet-map profile-config='{\"profile\":\"connectivity-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-UbT6S/JosIHXCB2ubdjB/4MisiPFz3cg7Qm+Zg3GDqSELJu6FK8bp+3bIErXkqMb\" crossorigin=\"anonymous\"></script>\n<cities-internet-map profile-config='{\"profile\":\"connectivity-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
}
},
{
@ -149,7 +149,7 @@
"scriptUrl": "./cities-internet/cities-internet.js",
"embed": {
"iframe": "<iframe src=\"./cities-internet/data-tables/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=\"./cities-internet/cities-internet.js\" integrity=\"sha384-cLy6LqX5n+KGC7VdympqtscDcHqEKchYgMh4GJbHO5Z6X9nYcD3IT7/3Crq/BGEE\" crossorigin=\"anonymous\"></script>\n<cities-internet-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-UbT6S/JosIHXCB2ubdjB/4MisiPFz3cg7Qm+Zg3GDqSELJu6FK8bp+3bIErXkqMb\" crossorigin=\"anonymous\"></script>\n<cities-internet-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
}
}
]
@ -259,7 +259,7 @@
"bundle": "demo-map",
"scriptUrl": "./demo-map/demo-map.js",
"output": "doxa-maps/demo-map/demo-map.js",
"integrity": "sha384-VGZMQWOWVouP4IkUTsZ/ElUORaUYu9+wJSlbs/8r6Lc6EU8VvLYHhmTOGCJv2Y6H",
"integrity": "sha384-1c8yrJm6kSIEfED30ZHPqjEr3oKC2cScuBz6jFh1UfcF6cHXeyYYiLCdh+qMxoaX",
"elements": [
"demo-map"
],
@ -275,7 +275,7 @@
"scriptUrl": "./demo-map/demo-map.js",
"embed": {
"iframe": "<iframe src=\"./demo-map/demo-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=\"./demo-map/demo-map.js\" integrity=\"sha384-VGZMQWOWVouP4IkUTsZ/ElUORaUYu9+wJSlbs/8r6Lc6EU8VvLYHhmTOGCJv2Y6H\" crossorigin=\"anonymous\"></script>\n<demo-map profile-config='{\"profile\":\"demo-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></demo-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./demo-map/demo-map.js\" integrity=\"sha384-1c8yrJm6kSIEfED30ZHPqjEr3oKC2cScuBz6jFh1UfcF6cHXeyYYiLCdh+qMxoaX\" crossorigin=\"anonymous\"></script>\n<demo-map profile-config='{\"profile\":\"demo-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></demo-map>"
},
"instances": [
{
@ -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-Cmdw0USq0X4/+OshXBayXKy4U5ZBD0bR8YR/yaR/CZ76CYEMpb4bymgfug+5AGyU",
"integrity": "sha384-j+2dQDsN+9KytIjgxVIRzcpXECccCuZhRT+CXc5B89vbo1IhaB8ogkO2Dc6KM1oy",
"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-Cmdw0USq0X4/+OshXBayXKy4U5ZBD0bR8YR/yaR/CZ76CYEMpb4bymgfug+5AGyU\" 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-j+2dQDsN+9KytIjgxVIRzcpXECccCuZhRT+CXc5B89vbo1IhaB8ogkO2Dc6KM1oy\" 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": [
{
@ -354,7 +354,7 @@
"bundle": "doxa-research-map",
"scriptUrl": "./doxa-research-map/doxa-research-map.js",
"output": "doxa-maps/doxa-research-map/doxa-research-map.js",
"integrity": "sha384-E7/WP0pDnwiG6noaZZI3Sx92Cg3JwluCqYOA5eU+x+UARKkliojcJBk3Yx6jua7a",
"integrity": "sha384-5E85ld1dX83U/sZ0feFMIW0Z+OQ6ZWcxirGRQyNeWegDShbqld62C5FoZbMwpP0m",
"elements": [
"doxa-research-map"
],
@ -370,7 +370,7 @@
"scriptUrl": "./doxa-research-map/doxa-research-map.js",
"embed": {
"iframe": "<iframe src=\"./doxa-research-map/research-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-research-map/doxa-research-map.js\" integrity=\"sha384-E7/WP0pDnwiG6noaZZI3Sx92Cg3JwluCqYOA5eU+x+UARKkliojcJBk3Yx6jua7a\" crossorigin=\"anonymous\"></script>\n<doxa-research-map profile-config='{\"profile\":\"research-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-research-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-research-map/doxa-research-map.js\" integrity=\"sha384-5E85ld1dX83U/sZ0feFMIW0Z+OQ6ZWcxirGRQyNeWegDShbqld62C5FoZbMwpP0m\" crossorigin=\"anonymous\"></script>\n<doxa-research-map profile-config='{\"profile\":\"research-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-research-map>"
},
"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-r/K0P50Wt1Qz2KkJ+fVPurKtAr68GqWKREiLb8DYZ2L0VmOQUE9FwZhrDMZzYscr",
"integrity": "sha384-rTSm6VQFqDXUXdPPFwhEFFblDLC4oFuoJ2Wtw8+iv/qAYC1ciiZlRSOsf+Ur4v6G",
"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-r/K0P50Wt1Qz2KkJ+fVPurKtAr68GqWKREiLb8DYZ2L0VmOQUE9FwZhrDMZzYscr\" 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-rTSm6VQFqDXUXdPPFwhEFFblDLC4oFuoJ2Wtw8+iv/qAYC1ciiZlRSOsf+Ur4v6G\" 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": [
{
@ -596,7 +596,7 @@
"bundle": "gospel-resources",
"scriptUrl": "./gospel-resources/gospel-resources.js",
"output": "doxa-maps/gospel-resources/gospel-resources.js",
"integrity": "sha384-VSJBa0rj+G/h4+0MOKOTCyB726MtzIp4EahuDM0P8rPU+27JIRcy7g8ILkiIZa7c",
"integrity": "sha384-tGmkizI0wLvGvK/ymOr0w5iXns+os1816InhYBq+759yZd9GZCzYLIgIGTmItyXL",
"elements": [
"gospel-resources-map"
],
@ -616,7 +616,7 @@
"scriptUrl": "./gospel-resources/gospel-resources.js",
"embed": {
"iframe": "<iframe src=\"./gospel-resources/charts/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=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-VSJBa0rj+G/h4+0MOKOTCyB726MtzIp4EahuDM0P8rPU+27JIRcy7g8ILkiIZa7c\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"charts\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-tGmkizI0wLvGvK/ymOr0w5iXns+os1816InhYBq+759yZd9GZCzYLIgIGTmItyXL\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"charts\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
}
},
{
@ -630,7 +630,7 @@
"scriptUrl": "./gospel-resources/gospel-resources.js",
"embed": {
"iframe": "<iframe src=\"./gospel-resources/data-tables/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=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-VSJBa0rj+G/h4+0MOKOTCyB726MtzIp4EahuDM0P8rPU+27JIRcy7g8ILkiIZa7c\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-tGmkizI0wLvGvK/ymOr0w5iXns+os1816InhYBq+759yZd9GZCzYLIgIGTmItyXL\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
}
},
{
@ -644,7 +644,7 @@
"scriptUrl": "./gospel-resources/gospel-resources.js",
"embed": {
"iframe": "<iframe src=\"./gospel-resources/gospel-dashboard/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=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-VSJBa0rj+G/h4+0MOKOTCyB726MtzIp4EahuDM0P8rPU+27JIRcy7g8ILkiIZa7c\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"gospel-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-tGmkizI0wLvGvK/ymOr0w5iXns+os1816InhYBq+759yZd9GZCzYLIgIGTmItyXL\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"gospel-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
}
},
{
@ -658,7 +658,7 @@
"scriptUrl": "./gospel-resources/gospel-resources.js",
"embed": {
"iframe": "<iframe src=\"./gospel-resources/resources-list/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=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-VSJBa0rj+G/h4+0MOKOTCyB726MtzIp4EahuDM0P8rPU+27JIRcy7g8ILkiIZa7c\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"resources-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-tGmkizI0wLvGvK/ymOr0w5iXns+os1816InhYBq+759yZd9GZCzYLIgIGTmItyXL\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"resources-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
}
},
{
@ -672,7 +672,7 @@
"scriptUrl": "./gospel-resources/gospel-resources.js",
"embed": {
"iframe": "<iframe src=\"./gospel-resources/resources-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=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-VSJBa0rj+G/h4+0MOKOTCyB726MtzIp4EahuDM0P8rPU+27JIRcy7g8ILkiIZa7c\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"resources-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-tGmkizI0wLvGvK/ymOr0w5iXns+os1816InhYBq+759yZd9GZCzYLIgIGTmItyXL\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"resources-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
}
}
]
@ -681,7 +681,7 @@
"bundle": "my-upg-100-list",
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
"output": "doxa-maps/my-upg-100-list/my-upg-100-list.js",
"integrity": "sha384-eEbOnVD6pMXOvGXN1VAnX8J3UfTAVruDCirw1585wIGTS+LwN/gCvupPPecye4yf",
"integrity": "sha384-4QjITklNF8dldIMyzCtJ89iM0orN98L0CNHUbBo04Vtis/xxe1qkkEFQkCF/04i7",
"elements": [
"my-upg-100-list-map"
],
@ -701,7 +701,7 @@
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
"embed": {
"iframe": "<iframe src=\"./my-upg-100-list/poster-wall/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=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-eEbOnVD6pMXOvGXN1VAnX8J3UfTAVruDCirw1585wIGTS+LwN/gCvupPPecye4yf\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-4QjITklNF8dldIMyzCtJ89iM0orN98L0CNHUbBo04Vtis/xxe1qkkEFQkCF/04i7\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
}
},
{
@ -715,7 +715,7 @@
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
"embed": {
"iframe": "<iframe src=\"./my-upg-100-list/upg-100-list/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=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-eEbOnVD6pMXOvGXN1VAnX8J3UfTAVruDCirw1585wIGTS+LwN/gCvupPPecye4yf\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-4QjITklNF8dldIMyzCtJ89iM0orN98L0CNHUbBo04Vtis/xxe1qkkEFQkCF/04i7\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
}
},
{
@ -729,7 +729,7 @@
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
"embed": {
"iframe": "<iframe src=\"./my-upg-100-list/upg-100-mfe/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=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-eEbOnVD6pMXOvGXN1VAnX8J3UfTAVruDCirw1585wIGTS+LwN/gCvupPPecye4yf\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-4QjITklNF8dldIMyzCtJ89iM0orN98L0CNHUbBo04Vtis/xxe1qkkEFQkCF/04i7\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
}
}
]
@ -799,7 +799,7 @@
"bundle": "plugin-studio",
"scriptUrl": "./plugin-studio/plugin-studio.js",
"output": "doxa-maps/plugin-studio/plugin-studio.js",
"integrity": "sha384-6Dmz25GPZmlHiB0aEzeuaYQvFfvy+1IMNy8Tsk9VflO2lH2543P/Q2W6WvetrU9o",
"integrity": "sha384-N70Sfwq4XTc7BHosJDT1r9/qUrzr1NZ5DiLb3QUYST8vY7+quBQzytqcpOs4Rp1E",
"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-6Dmz25GPZmlHiB0aEzeuaYQvFfvy+1IMNy8Tsk9VflO2lH2543P/Q2W6WvetrU9o\" 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-N70Sfwq4XTc7BHosJDT1r9/qUrzr1NZ5DiLb3QUYST8vY7+quBQzytqcpOs4Rp1E\" 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": [
{
@ -936,7 +936,7 @@
"bundle": "research-dashboard",
"scriptUrl": "./research-dashboard/research-dashboard.js",
"output": "doxa-maps/research-dashboard/research-dashboard.js",
"integrity": "sha384-hlPWMwkdwPg7Ze8QD4PwXTNv65fDKqwUOCrtX8NsH8h+rZHK1rZSnq43Vsz9O7Kt",
"integrity": "sha384-UND4Uv/KbN6Iuhev2tHmJwHWlq/sb5lNpe9+WUDGfiblyClUxRifJtuXRP9UlwIH",
"elements": [
"research-dashboard-map"
],
@ -952,7 +952,7 @@
"scriptUrl": "./research-dashboard/research-dashboard.js",
"embed": {
"iframe": "<iframe src=\"./research-dashboard/research-dashboard/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-dashboard/research-dashboard.js\" integrity=\"sha384-hlPWMwkdwPg7Ze8QD4PwXTNv65fDKqwUOCrtX8NsH8h+rZHK1rZSnq43Vsz9O7Kt\" crossorigin=\"anonymous\"></script>\n<research-dashboard-map profile-config='{\"profile\":\"research-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-dashboard-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-dashboard/research-dashboard.js\" integrity=\"sha384-UND4Uv/KbN6Iuhev2tHmJwHWlq/sb5lNpe9+WUDGfiblyClUxRifJtuXRP9UlwIH\" crossorigin=\"anonymous\"></script>\n<research-dashboard-map profile-config='{\"profile\":\"research-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-dashboard-map>"
},
"bundle": "research-dashboard"
}
@ -1050,7 +1050,7 @@
"bundle": "template-bundle",
"scriptUrl": "./template-bundle/template-bundle.js",
"output": "doxa-maps/template-bundle/template-bundle.js",
"integrity": "sha384-LhhbW7T6VzMBsasbX4ezhA2vembuHwp1cNM1WNooIHMdGp00x8rOr7F9NdMndaG4",
"integrity": "sha384-e6IcmUAvFTc057fmxLFzZmjVnt7Svy9M+S4gXgGbU9XgmssqVY1dwW5UkHI766a+",
"elements": [
"template-bundle"
],
@ -1066,7 +1066,7 @@
"scriptUrl": "./template-bundle/template-bundle.js",
"embed": {
"iframe": "<iframe src=\"./template-bundle/template-map-a/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=\"./template-bundle/template-bundle.js\" integrity=\"sha384-LhhbW7T6VzMBsasbX4ezhA2vembuHwp1cNM1WNooIHMdGp00x8rOr7F9NdMndaG4\" crossorigin=\"anonymous\"></script>\n<template-bundle profile-config='{\"profile\":\"template-map-a\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></template-bundle>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./template-bundle/template-bundle.js\" integrity=\"sha384-e6IcmUAvFTc057fmxLFzZmjVnt7Svy9M+S4gXgGbU9XgmssqVY1dwW5UkHI766a+\" crossorigin=\"anonymous\"></script>\n<template-bundle profile-config='{\"profile\":\"template-map-a\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></template-bundle>"
},
"bundle": "template-bundle"
},
@ -1081,7 +1081,7 @@
"scriptUrl": "./template-bundle/template-bundle.js",
"embed": {
"iframe": "<iframe src=\"./template-bundle/template-map-b/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=\"./template-bundle/template-bundle.js\" integrity=\"sha384-LhhbW7T6VzMBsasbX4ezhA2vembuHwp1cNM1WNooIHMdGp00x8rOr7F9NdMndaG4\" crossorigin=\"anonymous\"></script>\n<template-bundle profile-config='{\"profile\":\"template-map-b\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></template-bundle>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./template-bundle/template-bundle.js\" integrity=\"sha384-e6IcmUAvFTc057fmxLFzZmjVnt7Svy9M+S4gXgGbU9XgmssqVY1dwW5UkHI766a+\" crossorigin=\"anonymous\"></script>\n<template-bundle profile-config='{\"profile\":\"template-map-b\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></template-bundle>"
},
"bundle": "template-bundle"
}
@ -1091,7 +1091,7 @@
"bundle": "upg-neighbor",
"scriptUrl": "./upg-neighbor/upg-neighbor.js",
"output": "doxa-maps/upg-neighbor/upg-neighbor.js",
"integrity": "sha384-EwHRhn6v9ffMpvgViAzs6KpLdGI5FS0jhrU0E4bFfERGt0NywaxilnsRPQdcRcdX",
"integrity": "sha384-DVRFKBN7t+UQTjSwHz1FGzDSfgNRBA3e6WAeKCNn/9CUnzAtibMa1UG3X0Xqe7C/",
"elements": [
"upg-neighbor"
],
@ -1107,7 +1107,7 @@
"scriptUrl": "./upg-neighbor/upg-neighbor.js",
"embed": {
"iframe": "<iframe src=\"./upg-neighbor/upg-neighbor/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=\"./upg-neighbor/upg-neighbor.js\" integrity=\"sha384-EwHRhn6v9ffMpvgViAzs6KpLdGI5FS0jhrU0E4bFfERGt0NywaxilnsRPQdcRcdX\" crossorigin=\"anonymous\"></script>\n<upg-neighbor profile-config='{\"profile\":\"upg-neighbor\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></upg-neighbor>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./upg-neighbor/upg-neighbor.js\" integrity=\"sha384-DVRFKBN7t+UQTjSwHz1FGzDSfgNRBA3e6WAeKCNn/9CUnzAtibMa1UG3X0Xqe7C/\" crossorigin=\"anonymous\"></script>\n<upg-neighbor profile-config='{\"profile\":\"upg-neighbor\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></upg-neighbor>"
},
"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 <my-upg-100-list-map> custom element. -->
<script src="../my-upg-100-list.js" integrity="sha384-eEbOnVD6pMXOvGXN1VAnX8J3UfTAVruDCirw1585wIGTS+LwN/gCvupPPecye4yf" crossorigin="anonymous"></script>
<script src="../my-upg-100-list.js" integrity="sha384-4QjITklNF8dldIMyzCtJ89iM0orN98L0CNHUbBo04Vtis/xxe1qkkEFQkCF/04i7" 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 <my-upg-100-list-map> custom element. -->
<script src="../my-upg-100-list.js" integrity="sha384-eEbOnVD6pMXOvGXN1VAnX8J3UfTAVruDCirw1585wIGTS+LwN/gCvupPPecye4yf" crossorigin="anonymous"></script>
<script src="../my-upg-100-list.js" integrity="sha384-4QjITklNF8dldIMyzCtJ89iM0orN98L0CNHUbBo04Vtis/xxe1qkkEFQkCF/04i7" 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 <my-upg-100-list-map> custom element. -->
<script src="../my-upg-100-list.js" integrity="sha384-eEbOnVD6pMXOvGXN1VAnX8J3UfTAVruDCirw1585wIGTS+LwN/gCvupPPecye4yf" crossorigin="anonymous"></script>
<script src="../my-upg-100-list.js" integrity="sha384-4QjITklNF8dldIMyzCtJ89iM0orN98L0CNHUbBo04Vtis/xxe1qkkEFQkCF/04i7" 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

@ -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-6Dmz25GPZmlHiB0aEzeuaYQvFfvy+1IMNy8Tsk9VflO2lH2543P/Q2W6WvetrU9o" crossorigin="anonymous"></script>
<script src="../plugin-studio.js" integrity="sha384-N70Sfwq4XTc7BHosJDT1r9/qUrzr1NZ5DiLb3QUYST8vY7+quBQzytqcpOs4Rp1E" 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-6Dmz25GPZmlHiB0aEzeuaYQvFfvy+1IMNy8Tsk9VflO2lH2543P/Q2W6WvetrU9o" crossorigin="anonymous"></script>
<script src="../../plugin-studio.js" integrity="sha384-N70Sfwq4XTc7BHosJDT1r9/qUrzr1NZ5DiLb3QUYST8vY7+quBQzytqcpOs4Rp1E" 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 <research-dashboard-map> custom element. -->
<script src="../research-dashboard.js" integrity="sha384-hlPWMwkdwPg7Ze8QD4PwXTNv65fDKqwUOCrtX8NsH8h+rZHK1rZSnq43Vsz9O7Kt" crossorigin="anonymous"></script>
<script src="../research-dashboard.js" integrity="sha384-UND4Uv/KbN6Iuhev2tHmJwHWlq/sb5lNpe9+WUDGfiblyClUxRifJtuXRP9UlwIH" 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

@ -26,7 +26,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <template-bundle> custom element. -->
<script src="../template-bundle.js" integrity="sha384-LhhbW7T6VzMBsasbX4ezhA2vembuHwp1cNM1WNooIHMdGp00x8rOr7F9NdMndaG4" crossorigin="anonymous"></script>
<script src="../template-bundle.js" integrity="sha384-e6IcmUAvFTc057fmxLFzZmjVnt7Svy9M+S4gXgGbU9XgmssqVY1dwW5UkHI766a+" 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 <template-bundle> custom element. -->
<script src="../template-bundle.js" integrity="sha384-LhhbW7T6VzMBsasbX4ezhA2vembuHwp1cNM1WNooIHMdGp00x8rOr7F9NdMndaG4" crossorigin="anonymous"></script>
<script src="../template-bundle.js" integrity="sha384-e6IcmUAvFTc057fmxLFzZmjVnt7Svy9M+S4gXgGbU9XgmssqVY1dwW5UkHI766a+" 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 <upg-neighbor> custom element. -->
<script src="../../upg-neighbor.js" integrity="sha384-EwHRhn6v9ffMpvgViAzs6KpLdGI5FS0jhrU0E4bFfERGt0NywaxilnsRPQdcRcdX" crossorigin="anonymous"></script>
<script src="../../upg-neighbor.js" integrity="sha384-DVRFKBN7t+UQTjSwHz1FGzDSfgNRBA3e6WAeKCNn/9CUnzAtibMa1UG3X0Xqe7C/" 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 <upg-neighbor> custom element. -->
<script src="../../upg-neighbor.js" integrity="sha384-EwHRhn6v9ffMpvgViAzs6KpLdGI5FS0jhrU0E4bFfERGt0NywaxilnsRPQdcRcdX" crossorigin="anonymous"></script>
<script src="../../upg-neighbor.js" integrity="sha384-DVRFKBN7t+UQTjSwHz1FGzDSfgNRBA3e6WAeKCNn/9CUnzAtibMa1UG3X0Xqe7C/" 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 <upg-neighbor> custom element. -->
<script src="../upg-neighbor.js" integrity="sha384-EwHRhn6v9ffMpvgViAzs6KpLdGI5FS0jhrU0E4bFfERGt0NywaxilnsRPQdcRcdX" crossorigin="anonymous"></script>
<script src="../upg-neighbor.js" integrity="sha384-DVRFKBN7t+UQTjSwHz1FGzDSfgNRBA3e6WAeKCNn/9CUnzAtibMa1UG3X0Xqe7C/" 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 <upg-neighbor> custom element. -->
<script src="../../upg-neighbor.js" integrity="sha384-EwHRhn6v9ffMpvgViAzs6KpLdGI5FS0jhrU0E4bFfERGt0NywaxilnsRPQdcRcdX" crossorigin="anonymous"></script>
<script src="../../upg-neighbor.js" integrity="sha384-DVRFKBN7t+UQTjSwHz1FGzDSfgNRBA3e6WAeKCNn/9CUnzAtibMa1UG3X0Xqe7C/" 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 <upg-neighbor> custom element. -->
<script src="../../upg-neighbor.js" integrity="sha384-EwHRhn6v9ffMpvgViAzs6KpLdGI5FS0jhrU0E4bFfERGt0NywaxilnsRPQdcRcdX" crossorigin="anonymous"></script>
<script src="../../upg-neighbor.js" integrity="sha384-DVRFKBN7t+UQTjSwHz1FGzDSfgNRBA3e6WAeKCNn/9CUnzAtibMa1UG3X0Xqe7C/" 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 <upg-neighbor> custom element. -->
<script src="../../upg-neighbor.js" integrity="sha384-EwHRhn6v9ffMpvgViAzs6KpLdGI5FS0jhrU0E4bFfERGt0NywaxilnsRPQdcRcdX" crossorigin="anonymous"></script>
<script src="../../upg-neighbor.js" integrity="sha384-DVRFKBN7t+UQTjSwHz1FGzDSfgNRBA3e6WAeKCNn/9CUnzAtibMa1UG3X0Xqe7C/" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order: