Keep the site's own files as upstream has them
Outside the maps folder, the two hidden pages and the three maps, the site is upstream's again: the card styles, the embeddables' legend and the devtools setting are restored, the laptop notes and the embed test page are removed, and DX-MAPS-GALLERY.md is the guide to what this branch changes.
This commit is contained in:
parent
da3312ba17
commit
abbd86d5e7
8 changed files with 44 additions and 480 deletions
|
|
@ -1,51 +1,48 @@
|
|||
# Deep Research (the Research Gallery) on a hidden page, and every map served from /dx-maps/
|
||||
# Deep Research and Posterverse: the dx-maps output folder dropped into the site
|
||||
|
||||
This branch adds the **Deep Research** page, `/deep-research`: the Research Gallery (the research
|
||||
maps, dashboards and lists in one app), filling the page below the site header, on desktop and
|
||||
phone. The page is **hidden**: it is in no menu, and robots are told not to index it; it is reached
|
||||
by its address. The Research page is DX Life's own again, with the production simple map. Every map
|
||||
on the site loads from the same build tree.
|
||||
This branch drops the **dx-maps output folder** into `public/dx-maps/` and adds two **hidden pages** that
|
||||
show it as full-page apps. Every map on the site loads from that folder, on the site's own origin.
|
||||
|
||||
## What changed
|
||||
## What changes
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| `public/dx-maps/` | **The dx-maps output folder, dropped in** (doxa-maps `development` 53fcf6d09, `public/dx-maps/`, copied unchanged). Every map on this site loads from it, same-origin. |
|
||||
| `app/pages/deep-research/index.vue` | **Deep Research: the Research Gallery.** The page loads one script (`/dx-maps/doxa-maps-spa/doxa-maps-spa.js`) and adds one element (`<doxa-maps-spa>`), sized to the viewport below the header; its `tree-base` is built at runtime from the page's own origin. Every section of the gallery shows, the Experimental section included. Hidden: in no menu, `robots: noindex`. |
|
||||
| `app/pages/research/index.vue` | **The Research page, DX Life production's (fc4d622):** the heading, the "By Region" link, the research map on its own (`bundle="research-map"`, `<doxa-research-map>`) and the UUPG list. One addition, under the map: "Want Deeper Research? See our Deep-Research Maps →", linking to `/deep-research` (the locale prefix kept). The two phrases, and the page name "Deep Research", are in `i18n/locales/<code>/common.json` for all 11 locales; "Deep Research" stays a name in every language, as the gallery's own heading does. |
|
||||
| `app/pages/regions/index.vue`, `app/utils/nav.ts`, `nuxt.config.ts` | `/regions` is DX Life's own index again, with its menu item, and `/countries` goes to it (`USE_LEGACY_REGIONS_PAGE` and `LEGACY_REGIONS_PAGE` on). Set both off to send `/regions` to a gallery tab instead. |
|
||||
| `app/composables/useDoxaMap.ts` | The home (engagement), pray (prayer) and adopt (adoption) maps now load `/dx-maps/doxa-simple-map/doxa-simple-map.js`; the research-map key loads `/dx-maps/doxa-research-map/doxa-research-map.js`. Mapbox GL moves from v3.3.0 to v3.24.0, the version the dx-maps bundles are built and tested with. |
|
||||
| `app/components/DoxaMapSlot.vue` | With no Mapbox token configured, it asks the map for the keyless MapLibre engine (see **Map engine**). With a token, the config passes through unchanged. The map fills the slot's box, not the viewport (`--profile-min-h: 0`), so the slot never cuts off the map's attribution and bottom controls. |
|
||||
| `public/embed/map/index.html`, `public/research/doxa-research-map.html` | The same: bundles from `/dx-maps/`, Mapbox GL v3.24.0. |
|
||||
| `public/js/doxa-simple-map.js`, `public/js/doxa-research-map.js` | **Kept, unused.** No page loads them any more (every map loads from `/dx-maps/`), but they stay in place so nothing DX-life relied on is deleted. |
|
||||
| `public/dx-maps/` | **The dx-maps output folder, dropped in unchanged** (doxa-maps 53fcf6d09). Every map on the site loads from it. |
|
||||
| `app/pages/deep-research/index.vue` | **Deep Research** (`/deep-research`): every research map, dashboard and list in one app (`<doxa-maps-spa>`). A full-page app on every screen: no site header or footer, and the page itself never scrolls. The way back to the site is the app's own **Back to homepage** button, above **Collapse** in its menu. |
|
||||
| `app/pages/posterverse/index.vue` | **Posterverse** (`/posterverse`): the poster plugins in one app (`<posterverse-app>`), the same full-page way. |
|
||||
| `app/pages/research/index.vue` | The Research page keeps its research map. Under it, one new line, "Want Deeper Research? Deep Research app →", links to `/deep-research` in the reader's language. |
|
||||
| `app/composables/useDoxaMap.ts`, `app/components/DoxaMapSlot.vue`, `app/utils/mapProvider.ts` | The home (engagement), pray (prayer), adopt and research maps load their bundles from `/dx-maps/`: the same maps and presets as before. Their engine is MapLibre unless Mapbox is chosen (see **Map engine**): to keep today's Mapbox look, set `NUXT_PUBLIC_MAP_PROVIDER=mapbox` with the existing token. |
|
||||
| `public/embed/map/index.html`, `public/research/doxa-research-map.html` | The same: bundles from `/dx-maps/`. |
|
||||
| `nuxt.config.ts` | Old `/js/doxa-maps/…` addresses redirect to `/dx-maps/`, and the map settings below. |
|
||||
| `i18n/locales/*/common.json` | The new strings, in all 11 languages. |
|
||||
|
||||
Unchanged on purpose: the element names (`<doxa-map>`, `<doxa-research-map>`, `<doxa-maps-spa>`)
|
||||
and `nuxt.config.ts` (its `isCustomElement` lists only those names). `public/js/pplr-data-maps.iife.js`
|
||||
and `/research/pplr-data-maps.html` are a separate showcase with no dx-maps equivalent, so they are
|
||||
also unchanged.
|
||||
Unchanged: the regions index, the region and country pages and their maps, the header, the footer and the layout.
|
||||
`public/js/doxa-simple-map.js` and `public/js/doxa-research-map.js` stay in place, unused.
|
||||
|
||||
**Language.** The gallery follows the site's language and the language switcher (all 11 locales;
|
||||
Arabic lays out right-to-left).
|
||||
**Hidden pages.** `/deep-research` and `/posterverse` are in no menu, and robots are told not to index them.
|
||||
`/deep-research` is reached by the link under the Research page's map; `/posterverse` by its address.
|
||||
|
||||
**Map engine.** Every map on the site (the Deep Research gallery and the home, pray, adopt, research
|
||||
and country maps) uses the keyless MapLibre engine by default, so the whole site's maps work with no
|
||||
secrets at all. To switch back to Mapbox, as doxa.life uses it today, set
|
||||
`NUXT_PUBLIC_MAP_PROVIDER=mapbox` together with `NUXT_PUBLIC_MAPBOX_TOKEN`; a token alone does not
|
||||
switch, and "mapbox" without a token falls back to MapLibre. The page loads only the map bundle; each
|
||||
map loads its own engine, so nothing is fetched from api.mapbox.com unless Mapbox is chosen.
|
||||
**Language.** Both apps follow the site's language (all 11 locales; Arabic lays out right to left). Each app
|
||||
also has its own language menu (in the phone bar, and at the bottom of the menu on larger screens). A pick
|
||||
there fires a cancelable `doxa-language-request` event (`event.detail.language`). Both pages cancel it and
|
||||
switch the site's language (the language is in the address, `/ar/…`), so the site stays the one owner of the
|
||||
language, and the tab that was open carries over.
|
||||
|
||||
**What comes from elsewhere.** The map code and the data folder are this site's own (`/dx-maps/`). The
|
||||
MapLibre engine comes from jsDelivr, the base-map tiles from OpenFreeMap and VersaTiles, one
|
||||
dashboard's country figures from the World Bank API, and the maps' people-group data from the prayer
|
||||
API (`NUXT_PUBLIC_PRAY_BASE_URL`, default `https://pray.doxa.life`).
|
||||
**Map engine.** Every dx-maps map uses the keyless MapLibre engine by default, so the maps need no
|
||||
secrets. To use Mapbox, set `NUXT_PUBLIC_MAP_PROVIDER=mapbox` together with `NUXT_PUBLIC_MAPBOX_TOKEN`; a
|
||||
token alone does not switch, and "mapbox" without a token falls back to MapLibre.
|
||||
|
||||
**One scrollbar.** The gallery is a full-page app: it fills exactly the space between the header and
|
||||
the bottom of the screen. The Deep Research page has no footer and does not scroll itself, so the
|
||||
only scrollbar is the gallery's own.
|
||||
**Settings** (all optional):
|
||||
- `NUXT_PUBLIC_DX_MAPS_BASE`: load the maps from another copy of the folder (a URL; that host must send
|
||||
CORS headers). Unset = this site's own `/dx-maps/`.
|
||||
- `NUXT_PUBLIC_MAP_PROVIDER`, `NUXT_PUBLIC_MAPBOX_TOKEN`: the map engine, as above.
|
||||
- `NUXT_PUBLIC_PRAY_BASE_URL`: the prayer API the maps read people-group data from (default
|
||||
`https://pray.doxa.life`).
|
||||
|
||||
**Phone.** The gallery opens straight on a map (Simple Research). Its list of views sits behind the
|
||||
☰ button. Big Picture is marked **Desktop only** there (it needs a wide screen); every other view
|
||||
works on a phone.
|
||||
**What comes from elsewhere.** The map code and its data folder are this site's own (`/dx-maps/`). The
|
||||
MapLibre engine comes from jsDelivr, the base-map tiles from OpenFreeMap and VersaTiles, one dashboard's
|
||||
country figures from the World Bank API, the people-group data from the prayer API, and the Bible Posters'
|
||||
Bible text from bible.helloao.org (public-domain translations, credited to ebible.org).
|
||||
|
||||
## Run it
|
||||
|
||||
|
|
@ -54,37 +51,13 @@ bun install
|
|||
bun run dev
|
||||
```
|
||||
|
||||
Then open <http://localhost:3000/deep-research/> (Nuxt prints the port if 3000 is taken). No database
|
||||
or `.env` is needed for this page. Without `DATABASE_URL`, the dev server only logs
|
||||
"skipping migrations".
|
||||
Then open `/deep-research` and `/posterverse` (Nuxt prints the address). No database or `.env` is needed for
|
||||
these pages; without `DATABASE_URL` the dev server only logs "skipping migrations".
|
||||
|
||||
- Arabic: <http://localhost:3000/ar/deep-research/>
|
||||
- Open a specific view: `/deep-research/?tab=prayer-map` (also `research-map-plus-grid`,
|
||||
`regions-dashboard`, `upg-100`, and so on).
|
||||
- The Research page with the simple map: <http://localhost:3000/research/>
|
||||
- Share and embed links in the maps point at this site's own `/dx-maps/` pages.
|
||||
- To run it against a **local** dx-maps, and to try all three ways a site can take the gallery (drop-in,
|
||||
CDN, subdomain) with the exact ports and settings: [documentation/LAPTOP-6WAY.md](documentation/LAPTOP-6WAY.md).
|
||||
|
||||
## Deployment method: the dx-maps output folder dropped in (this branch, deep-research, from research-gallery-drop-in)
|
||||
|
||||
Identical to research-gallery-js (maps from the CDN), except that the dx-maps **output folder is dropped into `public/dx-maps/`** and
|
||||
every map loads **same-origin** from it: `NUXT_PUBLIC_DX_MAPS_BASE` defaults to empty, which means this
|
||||
site's own `/dx-maps/`. That covers the Research Gallery, the home (engagement), pray (prayer) and adopt maps
|
||||
(`useDoxaMap`, `DoxaMapSlot`), and the static pages `public/embed/map/` and
|
||||
`public/research/doxa-research-map.html` (which carry `/dx-maps` as a constant). Each map is told its
|
||||
`assetsBase` in the tree. No iframe is used, and no other host is involved.
|
||||
|
||||
## /embed-test
|
||||
|
||||
`/embed-test` embeds **every web plugin** in the dx-maps tree, as JavaScript (no iframe). The list is read
|
||||
at runtime from the tree's `manifest.json`, never typed: section 1 is every profile with `stage: production`,
|
||||
section 2 every other profile. A profile whose `targets` leave out `web` (a desktop-only plugin, such as
|
||||
Rudder) is skipped. Each card loads its bundle's script and adds the element that bundle defines. A card
|
||||
mounts its plugin when it scrolls into view and removes it when it leaves (browsers allow only a few WebGL
|
||||
maps at once). `?only=<bundle>/<profile>` (or a bundle name, comma-separated) shows just those.
|
||||
- Arabic: `/ar/deep-research`, `/ar/posterverse`
|
||||
- A specific view: `/deep-research?tab=prayer-map` (also `research-map-plus-grid`, `regions-dashboard`,
|
||||
`upg-100`, …), `/posterverse?tab=country-posters`
|
||||
|
||||
## Updating the maps
|
||||
|
||||
Replace `public/dx-maps/` with a newer build of the tree, from the doxa-maps repo's `public/dx-maps/`.
|
||||
No page needs to change.
|
||||
Replace `public/dx-maps/` with the `public/dx-maps/` folder of a newer doxa-maps build. No page needs to change.
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
# Deep Research on a laptop
|
||||
|
||||
Run this branch (`deep-research`) of the DX Life site on your own machine and look at the
|
||||
hidden Deep Research page and the Research page. No database, no Docker, no secrets.
|
||||
|
||||
## You need
|
||||
|
||||
- **bun 1.3+** and **node 20+**
|
||||
- **git**, and the address of the repository that holds this branch
|
||||
|
||||
## Steps
|
||||
|
||||
```bash
|
||||
git clone -b deep-research --single-branch <repository-url> dx-life-deep-research
|
||||
cd dx-life-deep-research
|
||||
bun install
|
||||
PORT=4611 bun run dev
|
||||
```
|
||||
|
||||
The first start compiles for about a minute; a page may answer 503 until it is done. Without a
|
||||
database the server only logs "skipping migrations" and the oauth sweeps' "DATABASE_URL is not set";
|
||||
the map pages do not use a database.
|
||||
|
||||
No settings are needed. The maps use MapLibre, which needs no key, and read their data from DX Life's
|
||||
prayer API (`https://pray.doxa.life`, the default).
|
||||
|
||||
## Open
|
||||
|
||||
| Page | Address |
|
||||
|---|---|
|
||||
| **Deep Research** (hidden: in no menu, not indexed) | <http://localhost:4611/deep-research> |
|
||||
| Deep Research in Arabic (right-to-left) | <http://localhost:4611/ar/deep-research> |
|
||||
| One view of it, e.g. the Regions dashboard | <http://localhost:4611/deep-research?tab=regions-dashboard> |
|
||||
| **Research** (as on doxa.life today, with our research map and, under it, the link to Deep Research) | <http://localhost:4611/research> |
|
||||
| Regions (DX Life's own index, back in the menu) | <http://localhost:4611/regions> |
|
||||
| Home, Pray, Adopt (DX Life's own maps, unchanged) | <http://localhost:4611/>, [/pray](http://localhost:4611/pray), [/adopt](http://localhost:4611/adopt) |
|
||||
| Plugin Studio (it is in the maps folder; no page needed) | <http://localhost:4611/dx-maps/plugin-studio/plugin-studio/> |
|
||||
|
||||
## Map engine: MapLibre by default, Mapbox by one setting
|
||||
|
||||
**Every map on the site uses MapLibre by default**, including DX Life's own maps on the home, pray,
|
||||
adopt and country pages. MapLibre needs no key, so the site, and any partner site, works with no
|
||||
token.
|
||||
|
||||
**To switch back to Mapbox**, as doxa.life uses it today, set one setting plus the token, then start
|
||||
the server:
|
||||
|
||||
```bash
|
||||
NUXT_PUBLIC_MAP_PROVIDER=mapbox NUXT_PUBLIC_MAPBOX_TOKEN=pk.your-token PORT=4611 bun run dev
|
||||
```
|
||||
|
||||
On a deployed site, set the same two variables in its environment. Without a token, "mapbox" falls
|
||||
back to MapLibre, so a map always starts.
|
||||
|
||||
## What is in this branch
|
||||
|
||||
- `/deep-research`: the Deep Research gallery (the DX Maps mobilization maps in one app, the
|
||||
Experimental section included), as a full-page app with no site header; its "Back to homepage" button sits
|
||||
above the menu's Collapse button. Every tab has an info button
|
||||
that explains what the map does.
|
||||
- `/research`: DX Life's production page (fc4d622), with the research map on its own, now loaded from
|
||||
this site's own `/dx-maps/`, and under it one new line: "Want Deeper Research? Deep Research
|
||||
app →", which links to `/deep-research` in the reader's language. The line is in all
|
||||
11 of the site's locales.
|
||||
- `/` and `/pray`: the engagement map and the prayer map (the simple map with those presets), as before.
|
||||
- `public/dx-maps/`: the DX Maps output folder, from DX Maps `development` 53fcf6d09.
|
||||
- The page never loads anything from api.mapbox.com; a map loads Mapbox itself, and only when Mapbox
|
||||
is chosen. A slow or blocked Mapbox cannot stop a map from appearing.
|
||||
|
||||
More detail: [DX-MAPS-GALLERY.md](DX-MAPS-GALLERY.md).
|
||||
|
|
@ -56,19 +56,10 @@
|
|||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: var(--border-radius-lg);
|
||||
// An <iframe> is composited in its own layer: `overflow: hidden` + `border-radius` on the
|
||||
// parent does not clip it in Chrome until the layer tree settles after the frame paints,
|
||||
// so the map showed square corners until it loaded and rounded ones after. clip-path is
|
||||
// applied at paint time regardless of compositing, and the iframe carries the radius too.
|
||||
clip-path: inset(0 round var(--border-radius-lg));
|
||||
isolation: isolate;
|
||||
|
||||
iframe {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 20rem;
|
||||
border: 0;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
|
|
|
|||
|
|
@ -1,191 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
// /embed-test — proves that EVERY dx-maps plugin built for the web embeds on this site as JavaScript
|
||||
// (no iframe). The list is read at runtime from the dx-maps tree's manifest.json, never typed here:
|
||||
// section 1 is every profile whose stage is "production", section 2 every other profile. A profile whose
|
||||
// manifest `targets` leaves out "web" (a desktop-only plugin) is skipped. So a plugin promoted, added or
|
||||
// retired changes this page by itself. Each card loads its bundle's script from the same tree the rest of the site uses and adds
|
||||
// the element the bundle defines, the way the manifest's embed.script snippet does.
|
||||
//
|
||||
// A map owns a WebGL context and a browser allows only a handful at once, so a card MOUNTS its
|
||||
// element when it scrolls into view and REMOVES it when it scrolls far away. ?only=<bundle>/<profile>
|
||||
// (comma-separated, or a bundle name) shows just those cards, for a single-plugin check.
|
||||
|
||||
import { mapboxTokenIfChosen } from '~/utils/mapProvider'
|
||||
useHead({ title: 'Embed test', meta: [{ name: 'robots', content: 'noindex' }] })
|
||||
|
||||
interface Card {
|
||||
key: string
|
||||
bundle: string
|
||||
profile: string
|
||||
tag: string
|
||||
script: string
|
||||
integrity: string
|
||||
description: string
|
||||
production: boolean
|
||||
locale: string // the language handed to the element (data-locale, for a probe)
|
||||
state: 'idle' | 'loading' | 'mounted' | 'error'
|
||||
error: string
|
||||
}
|
||||
|
||||
const runtimeConfig = useRuntimeConfig()
|
||||
const route = useRoute()
|
||||
const { locale } = useI18n() // the site's language, handed to every plugin (it is in the path, not ?lang)
|
||||
const cards = ref<Card[]>([])
|
||||
const status = ref('Reading the manifest…')
|
||||
const hosts = new Map<string, HTMLElement>()
|
||||
let observer: IntersectionObserver | null = null
|
||||
|
||||
// The dx-maps tree: NUXT_PUBLIC_DX_MAPS_BASE when set, otherwise this site's own /dx-maps/ copy.
|
||||
function treeBase(): string {
|
||||
const base = ((runtimeConfig.public as { dxMapsBase?: string }).dxMapsBase || '').replace(/\/+$/, '')
|
||||
return base || `${window.location.origin}/dx-maps`
|
||||
}
|
||||
const resolveUrl = (base: string, ref: string) =>
|
||||
/^(https?:)?\/\//.test(ref) ? ref : ref.startsWith('/') ? new URL(ref, base).href : `${base}/${ref.replace(/^\.\//, '')}`
|
||||
|
||||
const scripts = new Map<string, Promise<void>>()
|
||||
function loadScript(src: string, integrity: string): Promise<void> {
|
||||
if (!scripts.has(src)) {
|
||||
scripts.set(src, new Promise((resolve, reject) => {
|
||||
const s = document.createElement('script')
|
||||
s.src = src
|
||||
if (integrity) { s.integrity = integrity; s.crossOrigin = 'anonymous' }
|
||||
s.onload = () => resolve()
|
||||
s.onerror = () => reject(new Error(`the script did not load: ${src}`))
|
||||
document.head.appendChild(s)
|
||||
}))
|
||||
}
|
||||
return scripts.get(src)!
|
||||
}
|
||||
|
||||
// The element's config, in the site's CURRENT language: set on mount and again on every language switch,
|
||||
// so a mounted plugin follows the switcher (profile-config.locale for the maps, the language attribute for
|
||||
// the shells).
|
||||
function applyLocale(el: Element, card: Card) {
|
||||
const token = mapboxTokenIfChosen(runtimeConfig.public)
|
||||
el.setAttribute('profile-config', JSON.stringify({
|
||||
profile: card.profile,
|
||||
assetsBase: `${treeBase()}/assets`,
|
||||
locale: locale.value,
|
||||
// MapLibre by default; Mapbox only when NUXT_PUBLIC_MAP_PROVIDER=mapbox and a token is set
|
||||
...(token ? { tk: token } : { provider: 'maplibre' })
|
||||
}))
|
||||
el.setAttribute('language', locale.value)
|
||||
card.locale = locale.value
|
||||
}
|
||||
watch(locale, () => {
|
||||
for (const c of cards.value) {
|
||||
const el = c.state === 'mounted' ? hosts.get(c.key)?.firstElementChild : null
|
||||
if (el) applyLocale(el, c)
|
||||
}
|
||||
})
|
||||
|
||||
async function mount(card: Card) {
|
||||
const host = hosts.get(card.key)
|
||||
if (!host || card.state === 'mounted' || card.state === 'loading') return
|
||||
card.state = 'loading'
|
||||
try {
|
||||
await loadScript(card.script, card.integrity)
|
||||
if (!customElements.get(card.tag)) throw new Error(`<${card.tag}> was not defined by its bundle`)
|
||||
const el = document.createElement(card.tag)
|
||||
// shells (the Research Gallery, Plugin Studio) find their child maps through the tree's manifest
|
||||
el.setAttribute('tree-base', treeBase())
|
||||
applyLocale(el, card)
|
||||
host.replaceChildren(el)
|
||||
card.state = 'mounted'
|
||||
} catch (e) {
|
||||
card.state = 'error'
|
||||
card.error = String((e as Error)?.message || e)
|
||||
}
|
||||
}
|
||||
function unmount(card: Card) {
|
||||
if (card.state !== 'mounted') return
|
||||
hosts.get(card.key)?.replaceChildren()
|
||||
card.state = 'idle'
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
const base = treeBase()
|
||||
// Must be set before any bundle runs so the maps fetch from the configured prayer API.
|
||||
const pray = (runtimeConfig.public as { prayBaseUrl?: string }).prayBaseUrl
|
||||
if (pray) (window as unknown as { MAP_APP_API_URL?: string }).MAP_APP_API_URL = pray
|
||||
let manifest: { bundles?: Array<Record<string, any>> }
|
||||
try {
|
||||
const r = await fetch(`${base}/manifest.json`, { cache: 'no-cache' })
|
||||
if (!r.ok) throw new Error(`HTTP ${r.status}`)
|
||||
manifest = await r.json()
|
||||
} catch (e) {
|
||||
status.value = `The manifest did not load from ${base}/manifest.json (${(e as Error).message}).`
|
||||
return
|
||||
}
|
||||
const only = String(route.query.only || '').split(',').map((s) => s.trim()).filter(Boolean)
|
||||
const out: Card[] = []
|
||||
for (const b of manifest.bundles || []) {
|
||||
const defined: string[] = b.elements || []
|
||||
for (const p of b.profiles || []) {
|
||||
// desktop-only (or any non-web) plugins are not embeddable in a web page
|
||||
const targets: string[] | undefined = p.targets || b.targets
|
||||
if (Array.isArray(targets) && !targets.includes('web')) continue
|
||||
const key = `${b.bundle}/${p.profile}`
|
||||
if (only.length && !only.includes(key) && !only.includes(b.bundle)) continue
|
||||
// the tag the bundle actually defines (a profile's own element name can name one it does not)
|
||||
const tag = p.element && (!defined.length || defined.includes(p.element)) ? p.element : defined[0] || p.element
|
||||
out.push({ key, bundle: b.bundle, profile: p.profile, tag, script: resolveUrl(base, b.scriptUrl || `${b.bundle}/${b.bundle}.js`),
|
||||
integrity: b.integrity || '', description: p.description || '', production: (p.stage || b.stage) === 'production', locale: '',
|
||||
state: 'idle', error: '' })
|
||||
}
|
||||
}
|
||||
cards.value = [...out.filter((c) => c.production), ...out.filter((c) => !c.production)]
|
||||
const prod = out.filter((c) => c.production).length
|
||||
status.value = `${out.length} web plugins from ${base}/manifest.json (${prod} production, ${out.length - prod} other), each embedded as JavaScript.`
|
||||
await nextTick()
|
||||
observer = new IntersectionObserver((entries) => {
|
||||
for (const en of entries) {
|
||||
const card = cards.value.find((c) => c.key === (en.target as HTMLElement).dataset.card)
|
||||
if (!card) continue
|
||||
if (en.isIntersecting) mount(card)
|
||||
else unmount(card)
|
||||
}
|
||||
}, { rootMargin: '600px 0px' })
|
||||
document.querySelectorAll<HTMLElement>('[data-card]').forEach((n) => observer!.observe(n))
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
observer?.disconnect()
|
||||
hosts.forEach((h) => h.replaceChildren())
|
||||
})
|
||||
|
||||
const setHost = (key: string) => (el: unknown) => { if (el) hosts.set(key, el as HTMLElement); else hosts.delete(key) }
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container page-content embed-test stack stack--lg">
|
||||
<h1>Embed test</h1>
|
||||
<p data-embed-status>{{ status }}</p>
|
||||
<template v-for="(c, i) in cards" :key="c.key">
|
||||
<h2 v-if="i === 0 && c.production" data-embed-section="production">1. Production</h2>
|
||||
<h2 v-if="!c.production && (i === 0 || cards[i - 1].production)" data-embed-section="other">2. Every other web plugin</h2>
|
||||
<section class="embed-card" :data-card="c.key" :data-state="c.state" :data-production="c.production" :data-locale="c.locale">
|
||||
<header class="embed-card__head">
|
||||
<strong>{{ c.bundle }} / {{ c.profile }}</strong>
|
||||
<code><{{ c.tag }}></code>
|
||||
<span class="embed-card__state">{{ c.state }}</span>
|
||||
</header>
|
||||
<p v-if="c.description" class="embed-card__desc">{{ c.description }}</p>
|
||||
<p v-if="c.error" class="embed-card__error" role="alert">{{ c.error }}</p>
|
||||
<div :ref="setHost(c.key)" class="embed-card__host" />
|
||||
</section>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.embed-card { border: 1px solid #d1d4cd; border-radius: 10px; overflow: hidden; }
|
||||
.embed-card__head { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: baseline; padding: 10px 12px; background: #f3f3f1; }
|
||||
.embed-card__state { margin-inline-start: auto; font-size: 12px; color: #6d766e; }
|
||||
.embed-card__desc { margin: 0; padding: 6px 12px; font-size: 13px; color: #3b463d; }
|
||||
.embed-card__error { margin: 0; padding: 6px 12px; color: #a0302a; }
|
||||
.embed-card__host { position: relative; height: 560px; }
|
||||
.embed-card__host :deep(> *) { display: block; width: 100%; height: 100%; }
|
||||
@media (max-width: 600px) { .embed-card__host { height: 480px; } }
|
||||
</style>
|
||||
|
|
@ -1,140 +0,0 @@
|
|||
# The Research Gallery on your laptop: two routes
|
||||
|
||||
DX-life takes the dx-maps Research Gallery in **two routes**, one branch each. This page runs each of them on a
|
||||
laptop, so you can see and test them before any server is involved. The same two run on a shared server;
|
||||
together that is the embed test (2 routes × laptop and server).
|
||||
|
||||
| Route | DX-life branch | What runs | Docker | PocketBase |
|
||||
|---|---|---|---|---|
|
||||
| **Drop-in** (the default) | `research-gallery-drop-in` | this site alone; the maps are its own copy in `public/dx-maps` | no | no |
|
||||
| **Subdomain** | `research-gallery-subdomain` | the dx-maps **container** (Plugin Studio, PocketBase, the research site) + a local sign-in + this site | yes, about 5 GB | yes, for plugins that have users, an API or a database |
|
||||
|
||||
The **CDN** route (`research-gallery-cdn`) was retired on 2026-09-29. Its branch is kept for history.
|
||||
|
||||
## Before you start
|
||||
|
||||
- **Checkouts side by side:**
|
||||
```
|
||||
doxa-maps/ dx-maps, on development (work in progress) or main (production)
|
||||
doxa-life/ this repo
|
||||
```
|
||||
- **bun 1.3+** and **node 20+**. For the subdomain route, **Docker** with Compose: it uses about 1.4 GB, 2.5 GB while
|
||||
publishing; give Docker 4-6 GB.
|
||||
- **Once:** `cd doxa-life && bun install`. The branches share one lockfile.
|
||||
|
||||
## 1. Drop-in (the default: one server, no Docker)
|
||||
|
||||
```bash
|
||||
cd doxa-life && git switch research-gallery-drop-in
|
||||
PORT=4611 bun run dev
|
||||
```
|
||||
|
||||
Open http://localhost:4611/research/ (the Research Gallery) and http://localhost:4611/embed-test (every web
|
||||
plugin, embedded). There is **no setting to change**: the branch loads every map from its own `/dx-maps/` (same
|
||||
origin), a copy committed with it. The first `bun run dev` compiles for about a minute; a page may answer 503
|
||||
until it's done.
|
||||
|
||||
**To try a newer dx-maps** than the committed copy:
|
||||
|
||||
```bash
|
||||
cd doxa-maps && EMBED_BASE_URL=http://localhost:4611 bun run tree # seconds; only the two manifests change
|
||||
bun run dropin # copies dx-maps/ into ../doxa-life/public
|
||||
git restore public # dx-maps back to its committed folder
|
||||
```
|
||||
|
||||
Afterwards, put the site's copy back: `git restore public/dx-maps && git clean -fdq public/dx-maps`.
|
||||
|
||||
## 2. Subdomain (the dx-maps container, with PocketBase)
|
||||
|
||||
```bash
|
||||
cd doxa-life && git switch research-gallery-subdomain
|
||||
bun run laptop subdomain
|
||||
```
|
||||
|
||||
**The first time,** it makes `.env.laptop` (secrets, never committed) and stops if `GIT_TOKEN` is empty.
|
||||
- Put a read token for the git server that hosts the repositories there and run it again.
|
||||
- It builds dx-maps' container from `../doxa-maps/Dockerfile`, the image the shared server runs.
|
||||
- Pull dx-maps `development` first. The first start takes 5-10 minutes: the container clones, installs and
|
||||
publishes.
|
||||
|
||||
It starts three services, all on `127.0.0.1` only:
|
||||
|
||||
| Address | What |
|
||||
|---|---|
|
||||
| http://localhost:4613/ | this site, subdomain branch. The Research menu and `/regions` open the research site |
|
||||
| http://research.localhost:4615/ | the research site: the gallery at `/`, `/embed-test`, and the maps under `/dx-maps/`, which this site embeds |
|
||||
| http://localhost:4616/ | the Plugin Studio, PocketBase (`/pb`), `/cdn`, `/admin` |
|
||||
| http://localhost:4620/team | the local sign-in provider |
|
||||
|
||||
**Sign-in and roles**, the same as on the shared server:
|
||||
- Sign in to the Studio as **`admin`** (everything, including publishing and the PocketBase dashboard) or
|
||||
**`designer`** (the Studio and PocketBase's API).
|
||||
- Any other name is a plain team member, and is refused, as on the server.
|
||||
- There's no password: the provider is a local stand-in.
|
||||
|
||||
**How it is wired:**
|
||||
- `research.localhost` is your laptop's `research.<host>`. Chrome and Firefox send every `*.localhost` name to
|
||||
this computer. Safari may not: add `127.0.0.1 research.localhost` to `/etc/hosts`.
|
||||
- The sign-in provider runs inside the dx-maps container's network, so `localhost:4620` means the same to your
|
||||
browser and to the container. No hosts-file entry is needed for sign-in.
|
||||
- Publishing is automatic: a minute after `main` holds a new commit, the container rebuilds `/cdn` and the
|
||||
research site. The snippets point at `http://research.localhost:4615`.
|
||||
|
||||
**If memory is short,** the command says so before starting anything, and names what to close, or the lighter
|
||||
drop-in route.
|
||||
|
||||
**Stop:** `bun run laptop down` keeps PocketBase's data and the published build; `bun run laptop down --wipe`
|
||||
deletes them. `bun run laptop status` shows what's running.
|
||||
|
||||
## What "working" looks like
|
||||
|
||||
- **`/embed-test`:** the production cards mount and draw their maps, with 0 page errors and 0 iframes.
|
||||
`/ar/embed-test` runs the same test right to left.
|
||||
- **`/research/?tab=regions-dashboard`** opens the gallery on its Regions tab, and `/regions` redirects there.
|
||||
On the subdomain route that means `http://research.localhost:4615/?tab=regions-dashboard`.
|
||||
- **Headless**, a checker kept outside this repository (`check-embed-6way.mjs`) checks, for every production plugin:
|
||||
the script, its integrity hash, its element, its page, CORS, and the copy-paste snippet. The label reads
|
||||
`<name>=<site origin>@<manifest URL>`:
|
||||
- drop-in: `drop-in=http://localhost:4611@http://localhost:4611/dx-maps/manifest.json`;
|
||||
- subdomain: `subdomain=http://research.localhost:4615@http://research.localhost:4615/dx-maps/manifest.json`.
|
||||
- The **snippet** passes only when the manifests carry the address they're served from:
|
||||
- the subdomain route bakes it in by itself;
|
||||
- for the drop-in, regenerate the manifests as shown in 1.
|
||||
|
||||
## Without Docker, the subdomain route by hand (no PocketBase)
|
||||
|
||||
Two terminals. dx-maps serves its built folder (loopback only), and this site points its research links at it.
|
||||
|
||||
```bash
|
||||
cd doxa-maps && EMBED_BASE_URL=http://localhost:4610 bun run tree && PORT=4610 bun run serve # Terminal A
|
||||
cd doxa-life && git switch research-gallery-subdomain # Terminal B
|
||||
NUXT_PUBLIC_DX_MAPS_BASE=http://localhost:4610/dx-maps \
|
||||
NUXT_PUBLIC_RESEARCH_GALLERY_URL=http://research.localhost:4610/dx-maps/doxa-maps-spa/doxa-maps-spa/index.html \
|
||||
PORT=4613 bun run dev
|
||||
```
|
||||
|
||||
The dx-maps server does not list directories, so the gallery address ends in `/index.html`. Afterwards:
|
||||
`cd doxa-maps && git restore public`.
|
||||
|
||||
## Checked
|
||||
|
||||
The subdomain route was run on 2026-09-29 with this branch's `compose.laptop.yaml`.
|
||||
- Docker ran on a server, because the checking box has no Docker. Its ports were tunnelled to a Linux box's
|
||||
`localhost`, standing in for the laptop's browser.
|
||||
- The dx-maps image was built from `development`'s root `Dockerfile`. The container cloned `development` and, 2.5
|
||||
minutes after it started, published `main` (93061d2) by itself.
|
||||
|
||||
Results:
|
||||
- **20 of 20 route checks passed:**
|
||||
- the sign-in provider answers on the same address inside and outside;
|
||||
- a plain member is refused;
|
||||
- `designer` gets the Studio, but not the PocketBase dashboard or publishing;
|
||||
- `admin` gets all three;
|
||||
- PocketBase's team API is open, with sign-in through the team provider;
|
||||
- the research site serves the gallery at `/` and a manifest whose snippets point at
|
||||
`http://research.localhost:4615`;
|
||||
- `/regions` on this site goes to `http://research.localhost:4615/?tab=regions-dashboard`.
|
||||
- **`check-embed-6way` on `subdomain=http://research.localhost:4615@…/dx-maps/manifest.json`:** 7 of 7 production
|
||||
maps pass all 6 checks, the snippet included.
|
||||
- **Memory once running:** dx-maps 0.3 GB, this site 0.9 GB, the sign-in provider 0.15 GB. The first publish added
|
||||
about 1 GB for a minute.
|
||||
|
|
@ -26,6 +26,7 @@ dist/
|
|||
|
||||
# Internal engineering scratch — never commit
|
||||
.local-scratch/
|
||||
.lk10x/
|
||||
.agent-restricted
|
||||
build-status.md
|
||||
embeddables/**/intel/
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
/**
|
||||
* SemanticTreeLegend — reusable hierarchical legend for any one-to-many semantic tree.
|
||||
*
|
||||
* EXACT CLONE of the upstream PPLR module: a direct clone in which only the API is reduced. Only difference vs.
|
||||
* EXACT CLONE of the upstream PPLR module
|
||||
* per user directive: "do a direct clone, only shave the API." Only difference vs.
|
||||
* upstream is the <style scoped> block converted to useShadowStyles() so the CSS
|
||||
* lands inside doxa-research-mfe's shadow root.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -116,8 +116,7 @@ export default defineNuxtConfig({
|
|||
],
|
||||
|
||||
devtools: {
|
||||
// NUXT_DEVTOOLS=false turns the devtools overlay off (the cloud dev server sets it, for phone testing); unset = on, as before
|
||||
enabled: process.env.NUXT_DEVTOOLS !== 'false'
|
||||
enabled: true
|
||||
},
|
||||
|
||||
devServer: {
|
||||
|
|
|
|||
Loading…
Reference in a new issue