Correct the shell's account of how it mounts its tabs
Some checks are pending
build-gate / build (push) Waiting to run
Some checks are pending
build-gate / build (push) Waiting to run
The file header said tab 1 was bp-big-picture, mounted directly via its real tag. Both halves are false: tab 1 is dashboard-plus-grid, and every tab is an iframe — there is no direct-tag mount left in this shell. A reader trusting that text would look for a mount path that does not exist and maintain the wrong bundle believing it was the one users see. The big-picture bundle is still a valid element; it is simply one of the parts already composed into the tab-1 composite. The locale switcher also poked slot zero by hand, writing a profile-config named big-picture onto what is now an iframe, which ignores it. Harmless, and that is why it survived: the effectiveLocale watcher was doing the real work all along, branching on the tab's mode and re-pointing the iframe src that carries the locale. Removed rather than repaired, since a second hardcoded locale path is one more thing to keep in sync for nothing. Verified by driving the switcher: the active frame moves from locale=en to locale=es. The direct-tag branch in the template is kept. It is generic, keyed on the tab's mode rather than a bundle name, and remains the right way to nest a future token-free element. Only the comment claiming tab 1 uses it was wrong.
This commit is contained in:
parent
498bf9409c
commit
94dc3c64e8
9 changed files with 58 additions and 40 deletions
|
|
@ -10,17 +10,26 @@
|
|||
as its tabs. This IS the DX-maps app: the unified place a user opens to reach every research
|
||||
map without getting confused by a dozen separate embeds.
|
||||
|
||||
TAB 1 = Big Picture (Cat-4 composite <bp-big-picture>) — the header (shared SearchBar + 4
|
||||
FilterDropdowns) driving the real people-groups grid. Token-free + self-contained, so it is
|
||||
mounted DIRECTLY via its real tag + its built .js (direct-JS nesting): the SPA injects
|
||||
big-picture/big-picture.js ONCE (guarded), then creates <bp-big-picture>. This honors the
|
||||
card's "load each nested element by its real tag + its built .js".
|
||||
TAB 1 = Big Picture — the cap-4 composite `dashboard-plus-grid`: the research dashboard with the
|
||||
real people-groups grid stacked beneath it, scrolled as one element.
|
||||
|
||||
TABS 2..N = every production research map — each nested as its OWN full SPA via that map's
|
||||
standalone leaf page inside an <iframe>. WHY an iframe for the map tabs and a direct tag for
|
||||
tab-1? The map bundles each register their OWN customElements.define (one-shot) AND need the
|
||||
Mapbox peer-engine + token ladder; loading many such IIFEs into ONE document is the one-shot-
|
||||
define hazard the research flagged. Giving each map its own document (its leaf page, which is
|
||||
★ CORRECTED 2026-09-22. This header used to say tab-1 was <bp-big-picture>, "mounted DIRECTLY
|
||||
via its real tag + its built .js (direct-JS nesting)". BOTH halves are now false and were
|
||||
measured so: tab-1 is `dashboard-plus-grid` (see TAB_DEFS), and EVERY tab is mode:'iframe' —
|
||||
there is no direct-tag mount left anywhere in this shell. The `big-picture` bundle still exists
|
||||
and is still a valid cap-4 element, but it is NOT what this SPA opens on; it sits in
|
||||
NOT_EXPERIMENTAL as one of the PARTS already composed into the tab-1 composite. A reader who
|
||||
trusted the old text would go looking for a direct-mount path that no longer exists, and would
|
||||
maintain the wrong bundle believing it was the one users see.
|
||||
|
||||
TABS 1..N = every tab is nested as its OWN full SPA via that bundle's standalone leaf page inside
|
||||
an <iframe>. WHY an iframe rather than direct-tag nesting? The map bundles each register their
|
||||
OWN customElements.define (one-shot) AND need the Mapbox peer-engine + token ladder; loading
|
||||
many such IIFEs into ONE document is the one-shot-define hazard the research flagged. There is a
|
||||
second reason that applies to EVERY tab, not just maps: window.MAP_TOKEN does not cross an
|
||||
iframe boundary, so each leaf carries its own baked token (gated by check:nested-token) and a
|
||||
nested map cannot be starved by whatever the parent document happens to hold. Giving each tab
|
||||
its own document (its leaf page, which is
|
||||
already a fully self-contained SPA with its own peer-engine + token ladder + profile-config)
|
||||
SIDESTEPS that hazard entirely, keeps each map individually correct, and is position-
|
||||
independent (leaf pages resolve everything relative to the tree root). Each iframe is lazy —
|
||||
|
|
@ -34,8 +43,9 @@
|
|||
back to a same-tree relative guess so a plain drop still resolves.
|
||||
|
||||
NO re-import of any nested plugin's components and NO re-registration of a tag another bundle
|
||||
owns — the only tag this bundle defines is <doxa-maps-spa> (see index.js). bp-big-picture is
|
||||
registered by big-picture.js when that IIFE loads; this SPA only USES the tag.
|
||||
owns — the only tag this bundle defines is <doxa-maps-spa> (see index.js). Every nested bundle
|
||||
registers its own tag inside its own iframe document, so a one-shot customElements.define in one
|
||||
tab can never collide with another's.
|
||||
-->
|
||||
<template>
|
||||
<!-- The SHELL (top bar + vertical tab rail + content frame) is the ONE shared beautiful layout,
|
||||
|
|
@ -99,11 +109,20 @@
|
|||
v-for="(t, i) in tabs" :key="t.id" class="dms-pane"
|
||||
v-show="i === activeIndex" :hidden="i !== activeIndex">
|
||||
|
||||
<!-- TAB 1: direct-tag nesting of the token-free composite. The real <bp-big-picture>
|
||||
is created imperatively into an EMPTY, Vue-unmanaged slot div (:ref, no children)
|
||||
after big-picture.js is injected once — so Vue never fights the custom element's
|
||||
own shadow DOM. The loading/error hint is a SEPARATE overlay sibling, so clearing
|
||||
the slot with innerHTML never removes a Vue-managed node. -->
|
||||
<!-- DIRECT-TAG NESTING (mode:'element') — the path for a TOKEN-FREE element that can safely
|
||||
share this document. The tag is created imperatively into an EMPTY, Vue-unmanaged slot
|
||||
div (:ref, no children) after that bundle's .js is injected once, so Vue never fights the
|
||||
custom element's own shadow DOM. The loading/error hint is a SEPARATE overlay sibling, so
|
||||
clearing the slot with innerHTML never removes a Vue-managed node.
|
||||
|
||||
★ NO TAB CURRENTLY USES THIS (measured 2026-09-22): every entry in TAB_DEFS is
|
||||
mode:'iframe'. This comment used to say "TAB 1: ... The real <bp-big-picture>", which was
|
||||
doubly wrong — tab-1 is `dashboard-plus-grid`, and it is iframed like the rest. The
|
||||
branch is KEPT, not deleted: it is generic (keyed on t.mode, not on a bundle name) and it
|
||||
is the correct way to nest a future token-free element without paying for a document.
|
||||
Anything that owns a map must stay in the iframe path — one-shot customElements.define
|
||||
collisions, the Mapbox peer engine, and the fact that window.MAP_TOKEN does not cross an
|
||||
iframe boundary. -->
|
||||
<template v-if="t.mode === 'element'">
|
||||
<div class="dms-slot" :ref="el => setSlot(i, el)"></div>
|
||||
<div v-if="t.state === 'loading'" class="dms-hint">Loading {{ t.label }}…</div>
|
||||
|
|
@ -112,7 +131,7 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<!-- TABS 2..N: each map as its own full SPA in a lazy iframe (src set on first activation). -->
|
||||
<!-- EVERY TAB TODAY: its own full SPA in a lazy iframe (src set on first activation). -->
|
||||
<iframe
|
||||
v-else
|
||||
class="dms-frame"
|
||||
|
|
@ -678,23 +697,22 @@ export default defineComponent({
|
|||
.catch(() => { /* non-fatal: Experimental simply stays empty */ })
|
||||
} catch { /* fetch unavailable (non-browser): same fail-soft path */ }
|
||||
},
|
||||
// Switch UI language. languages.js's contract: switching = re-setting profile-config.locale on
|
||||
// the mounted element (ProfileLoader reads it, drives the bundle's i18n). We persist the choice
|
||||
// and best-effort re-set the locale on tab-1's mounted <bp-big-picture> (the only direct element;
|
||||
// the iframe maps carry their own locale ladder). No router, no dependency.
|
||||
// Switch UI language. Persist the choice and let the `effectiveLocale` WATCHER apply it: that
|
||||
// watcher already branches on the active tab's mode — re-point an iframe src (which carries
|
||||
// ?locale=), or re-set profile-config on a direct element — generically, by t.id.
|
||||
//
|
||||
// ★ THIS ALSO USED TO POKE SLOT 0 BY HAND, and that block was stale in two ways at once
|
||||
// (corrected 2026-09-22). It wrote a profile-config naming 'big-picture' onto slot 0's first
|
||||
// child, described in its own comment as "tab-1's mounted <bp-big-picture> (the only direct
|
||||
// element)". MEASURED: every tab is now mode:'iframe' — there is no direct element left — and
|
||||
// tab-1 is `dashboard-plus-grid`, not `big-picture`. So it set an attribute an <iframe> ignores,
|
||||
// naming a bundle that is not tab-1. Harmless, which is exactly why it survived: the watcher was
|
||||
// doing the real work the whole time. Removed rather than repaired — a second hardcoded locale
|
||||
// path is one more thing to keep in sync for no gain.
|
||||
setLocale(code) {
|
||||
this.locale = code
|
||||
this.localeOpen = false
|
||||
saveLocale(code)
|
||||
try {
|
||||
const slot = this._slots && this._slots[0]
|
||||
const el = slot && slot.firstElementChild
|
||||
if (el && el.setAttribute) {
|
||||
const pc = { profile: 'big-picture', instanceId: 'dms-big-picture', locale: code }
|
||||
if (this.tk || this.cfg.tk) pc.tk = this.tk || this.cfg.tk
|
||||
el.setAttribute('profile-config', JSON.stringify(pc))
|
||||
}
|
||||
} catch { /* non-fatal: the switcher still persists + updates the UI */ }
|
||||
},
|
||||
// The shared rail emits the selected tab's `key` (its id); map it back to the index the
|
||||
// pane-mount machinery below is keyed on.
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
pid=2317786 started=2026-09-22T08:07:49Z by=lk10x
|
||||
pid=2427619 started=2026-09-22T08:27:28Z by=lk10x
|
||||
|
|
|
|||
2
public/doxa-maps/doxa-maps-spa/doxa-maps-spa.js
generated
2
public/doxa-maps/doxa-maps-spa/doxa-maps-spa.js
generated
File diff suppressed because one or more lines are too long
|
|
@ -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-pmn3IM3nPXHdhYvfyIW11PrG4UhEkINrGQdzwi2vYl023Lomm9bRTeIpC4lh0lCl" crossorigin="anonymous"></script>
|
||||
<script src="../../doxa-maps-spa.js" integrity="sha384-eUOhrqzBqC4p9TmS3XkDuKPTjgRooQV1WrhUFaa8p3NqmKUXmEfbwh8GxfB5AK90" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -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-pmn3IM3nPXHdhYvfyIW11PrG4UhEkINrGQdzwi2vYl023Lomm9bRTeIpC4lh0lCl" crossorigin="anonymous"></script>
|
||||
<script src="../doxa-maps-spa.js" integrity="sha384-eUOhrqzBqC4p9TmS3XkDuKPTjgRooQV1WrhUFaa8p3NqmKUXmEfbwh8GxfB5AK90" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
2
public/doxa-maps/doxa-maps-spa/index.html
generated
2
public/doxa-maps/doxa-maps-spa/index.html
generated
|
|
@ -130,7 +130,7 @@ header.top .sub { margin-top: 3px; font: 12px/1.4 var(--sans); color: var(--mut)
|
|||
|
||||
<script>window.FORGE = {"mode":"built","scope":"bundle","manifestUrl":"../manifest.json","bundle":"doxa-maps-spa","hrefBase":"../"};
|
||||
/* file:// contract: manifest inlined at build — no fetch needed to render. */
|
||||
window.FORGE.manifest = {"schema":"doxa-maps-manifest@1","generatedFrom":"plugin-designs/*/*.vue","embedBase":".","bundleCount":19,"profileCount":28,"bundles":[{"bundle":"doxa-maps-spa","scriptUrl":"./doxa-maps-spa/doxa-maps-spa.js","output":"doxa-maps/doxa-maps-spa/doxa-maps-spa.js","integrity":"sha384-pmn3IM3nPXHdhYvfyIW11PrG4UhEkINrGQdzwi2vYl023Lomm9bRTeIpC4lh0lCl","elements":["doxa-maps-spa"],"profiles":[{"profile":"doxa-maps-spa","file":"doxa-maps-spa.vue","description":"The DX-maps app itself: one element whose vertical-tab rail nests every other","element":"doxa-maps-spa","type":"nested-mfe-spa","isMap":false,"iframeUrl":"./doxa-maps-spa/doxa-maps-spa/index.html","scriptUrl":"./doxa-maps-spa/doxa-maps-spa.js","embed":{"iframe":"\u003ciframe src=\"./doxa-maps-spa/doxa-maps-spa/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./doxa-maps-spa/doxa-maps-spa.js\" integrity=\"sha384-pmn3IM3nPXHdhYvfyIW11PrG4UhEkINrGQdzwi2vYl023Lomm9bRTeIpC4lh0lCl\" crossorigin=\"anonymous\">\u003c/script>\n\u003cdoxa-maps-spa profile-config='{\"profile\":\"doxa-maps-spa\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/doxa-maps-spa>"},"instances":[{"id":"app","title":"DX-Maps SPA — Big Picture + every research map (app-shell)","apiBaseUrl":"","props":{"profile":"doxa-maps-spa","provider":"maplibre","instanceId":"doxa-maps-spa-app","title":"DX-Maps"},"element":"doxa-maps-spa","iframeUrl":"./doxa-maps-spa/doxa-maps-spa/app/index.html","embed":{"iframe":"\u003ciframe src=\"./doxa-maps-spa/doxa-maps-spa/app/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}}]}]}]};</script>
|
||||
window.FORGE.manifest = {"schema":"doxa-maps-manifest@1","generatedFrom":"plugin-designs/*/*.vue","embedBase":".","bundleCount":19,"profileCount":28,"bundles":[{"bundle":"doxa-maps-spa","scriptUrl":"./doxa-maps-spa/doxa-maps-spa.js","output":"doxa-maps/doxa-maps-spa/doxa-maps-spa.js","integrity":"sha384-eUOhrqzBqC4p9TmS3XkDuKPTjgRooQV1WrhUFaa8p3NqmKUXmEfbwh8GxfB5AK90","elements":["doxa-maps-spa"],"profiles":[{"profile":"doxa-maps-spa","file":"doxa-maps-spa.vue","description":"The DX-maps app itself: one element whose vertical-tab rail nests every other","element":"doxa-maps-spa","type":"nested-mfe-spa","isMap":false,"iframeUrl":"./doxa-maps-spa/doxa-maps-spa/index.html","scriptUrl":"./doxa-maps-spa/doxa-maps-spa.js","embed":{"iframe":"\u003ciframe src=\"./doxa-maps-spa/doxa-maps-spa/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./doxa-maps-spa/doxa-maps-spa.js\" integrity=\"sha384-eUOhrqzBqC4p9TmS3XkDuKPTjgRooQV1WrhUFaa8p3NqmKUXmEfbwh8GxfB5AK90\" crossorigin=\"anonymous\">\u003c/script>\n\u003cdoxa-maps-spa profile-config='{\"profile\":\"doxa-maps-spa\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/doxa-maps-spa>"},"instances":[{"id":"app","title":"DX-Maps SPA — Big Picture + every research map (app-shell)","apiBaseUrl":"","props":{"profile":"doxa-maps-spa","provider":"maplibre","instanceId":"doxa-maps-spa-app","title":"DX-Maps"},"element":"doxa-maps-spa","iframeUrl":"./doxa-maps-spa/doxa-maps-spa/app/index.html","embed":{"iframe":"\u003ciframe src=\"./doxa-maps-spa/doxa-maps-spa/app/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}}]}]}]};</script>
|
||||
<script>/**
|
||||
* staging-forge.js — the ONE-PAGE semantic staging environment (root + bundle pages).
|
||||
*
|
||||
|
|
|
|||
2
public/doxa-maps/index.html
generated
2
public/doxa-maps/index.html
generated
File diff suppressed because one or more lines are too long
4
public/doxa-maps/manifest.json
generated
4
public/doxa-maps/manifest.json
generated
|
|
@ -272,7 +272,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-pmn3IM3nPXHdhYvfyIW11PrG4UhEkINrGQdzwi2vYl023Lomm9bRTeIpC4lh0lCl",
|
||||
"integrity": "sha384-eUOhrqzBqC4p9TmS3XkDuKPTjgRooQV1WrhUFaa8p3NqmKUXmEfbwh8GxfB5AK90",
|
||||
"elements": [
|
||||
"doxa-maps-spa"
|
||||
],
|
||||
|
|
@ -288,7 +288,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-pmn3IM3nPXHdhYvfyIW11PrG4UhEkINrGQdzwi2vYl023Lomm9bRTeIpC4lh0lCl\" 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-eUOhrqzBqC4p9TmS3XkDuKPTjgRooQV1WrhUFaa8p3NqmKUXmEfbwh8GxfB5AK90\" 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": [
|
||||
{
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue