From 2d64e21fbf75154f083ab31f18a6b0b3d335b3b2 Mon Sep 17 00:00:00 2001 From: doxa-maps-88655c70 <88627970+doxa-maps-88655c70@users.noreply.github.com> Date: Thu, 17 Sep 2026 18:18:19 +0000 Subject: [PATCH] Complete app-profiles->plugin-designs sweep across comments/docs; build green; layouts kept at root (load-bearing) --- CONTRIBUTING.md | 4 +- README.md | 8 +- contributing/COLORS.md | 4 +- contributing/README.md | 4 +- contributing/staging-reference-design.html | 2 +- data/README.md | 6 +- docs/architecture/app-wrappers.md | 4 +- docs/architecture/color-strategies.md | 4 +- docs/architecture/file-map.md | 52 +++--- docs/architecture/i18n.md | 2 +- docs/architecture/patterns-and-provenance.md | 2 +- docs/architecture/poster-generation.md | 2 +- docs/explanation.md | 2 +- docs/how-to.md | 16 +- docs/reference.md | 22 +-- docs/tutorial.md | 18 +-- index.html | 2 +- internal/generate-gallery.mjs | 10 +- internal/generate-library-index.mjs | 4 +- internal/generate-tree.mjs | 4 +- internal/legend-columns-probe.mjs | 2 +- internal/long-frames-probe.mjs | 6 +- internal/mobile-sheet-probe.mjs | 4 +- internal/plugin-gallery/dev-plugin.mjs | 12 +- internal/plugin-gallery/discover.mjs | 4 +- internal/staging-forge.js | 4 +- internal/staging.js | 12 +- .../vite-plugin-strip-shadow-css-comments.mjs | 4 +- layouts/README.md | 2 +- layouts/app-wrapper-material/index.html | 8 +- library/ProfileLoader.vue | 2 +- library/README.md | 4 +- library/colors/README.md | 2 +- library/colors/_registry.js | 2 +- library/components/WARNING.md | 4 +- .../legends/researchLegendOptions.js | 2 +- .../legends/useAffinityBlockLegendData.js | 2 +- library/composables/legends/useLegendData.js | 4 +- library/composables/useGeocoderSearch.js | 2 +- library/scripts/posters/emit-countries.mjs | 2 +- library/scripts/posters/generate.mjs | 6 +- library/scripts/posters/make-previews.py | 4 +- .../_granular-experiment.mjs.shelved | 4 +- .../snapshots/v1-3x4-portrait/generate.mjs | 2 +- .../snapshots/v2-3x3-full-vision/README.md | 4 +- .../v3-3x3-thumbnail-template/README.md | 4 +- .../v3-3x3-thumbnail-template/generate.mjs | 2 +- .../snapshots/v4-3x3-refined/generate.mjs | 2 +- .../snapshots/v5-3x3-approved/README.md | 2 +- .../snapshots/v5-3x3-approved/generate.mjs | 4 +- .../research/build-language-resources.mjs | 4 +- .../research/build-tabulator-tables.mjs | 2 +- library/scripts/research/verify-map-paint.mjs | 4 +- library/utils/DataSourceManager.js | 2 +- plugin-designs/README.md | 14 +- plugin-designs/all-people-groups/index.js | 2 +- plugin-designs/cities-internet/index.js | 2 +- plugin-designs/country-posters/index.js | 2 +- plugin-designs/demo-map/index.js | 4 +- plugin-designs/doxa-research-map/index.html | 2 +- plugin-designs/doxa-simple-map/index.html | 2 +- plugin-designs/doxa-simple-map/index.js | 2 +- plugin-designs/gospel-resources/charts.vue | 2 +- plugin-designs/gospel-resources/index.js | 2 +- .../scripts/verify-gospel-click-sheet.mjs | 4 +- .../scripts/verify-gospel-links.mjs | 2 +- plugin-designs/key-cities/index.js | 2 +- .../my-upg-100-list/data/adjacency-proof.mjs | 2 +- .../my-upg-100-list/data/clusters.source.md | 2 +- .../data/color-isolation-proof.mjs | 2 +- .../data/generate-region-shades.mjs | 4 +- .../data/oceania-color-proof.mjs | 2 +- .../my-upg-100-list/data/palette-preview.mjs | 10 +- .../my-upg-100-list/data/pip-proof.mjs | 2 +- .../my-upg-100-list/data/pip-stamp.mjs | 2 +- plugin-designs/my-upg-100-list/data/proof.sh | 4 +- .../my-upg-100-list/data/region-hue-proof.mjs | 2 +- .../my-upg-100-list/data/retag-adm1.mjs | 2 +- .../my-upg-100-list/data/set-zone-color.mjs | 4 +- .../data/source-boundaries.mjs | 12 +- .../my-upg-100-list/data/visual-gate.mjs | 4 +- .../data/zone-resolves-proof.mjs | 2 +- plugin-designs/my-upg-100-list/index.js | 2 +- plugin-designs/research-dashboard/index.js | 2 +- .../src/composables/useDashboardLayout.js | 2 +- plugin-designs/template-bundle/README.md | 8 +- plugin-designs/template-bundle/index.js | 2 +- .../template-bundle/template-map-a.vue | 2 +- .../all-people-groups/all-people-groups.js | 4 +- .../all-people-groups/index.html | 2 +- public/doxa-maps/all-people-groups/index.html | 6 +- .../cities-internet/cities-internet.js | 4 +- .../connectivity-list/index.html | 2 +- .../connectivity-map/index.html | 2 +- .../cities-internet/data-tables/index.html | 2 +- public/doxa-maps/cities-internet/index.html | 6 +- .../country-posters/countries-map/index.html | 2 +- .../country-poster-wall/index.html | 2 +- .../country-posters/country-posters.js | 68 +------- public/doxa-maps/country-posters/index.html | 6 +- public/doxa-maps/demo-map/demo-map.js | 39 +++-- public/doxa-maps/demo-map/demo-map/index.html | 2 +- .../demo-map/demo-map/maplibre/index.html | 2 +- public/doxa-maps/demo-map/index.html | 6 +- .../doxa-research-map/doxa-research-map.js | 152 ++++-------------- public/doxa-maps/doxa-research-map/index.html | 6 +- .../doxa-research-map/research-map/index.html | 2 +- .../doxa-simple-map/doxa-simple-map.js | 131 ++++----------- .../doxa-simple-map/adoption/index.html | 2 +- .../doxa-simple-map/engagement/index.html | 2 +- .../doxa-simple-map/index.html | 2 +- .../doxa-simple-map/prayer/index.html | 2 +- public/doxa-maps/doxa-simple-map/index.html | 6 +- .../gospel-resources/charts/index.html | 2 +- .../gospel-resources/data-tables/index.html | 2 +- .../gospel-resources/gospel-resources.js | 4 +- public/doxa-maps/gospel-resources/index.html | 6 +- .../resources-list/index.html | 2 +- .../gospel-resources/resources-map/index.html | 2 +- public/doxa-maps/index.html | 6 +- public/doxa-maps/key-cities/key-cities.js | 4 +- public/doxa-maps/manifest.json | 58 +++---- public/doxa-maps/my-upg-100-list/index.html | 6 +- .../my-upg-100-list/my-upg-100-list.js | 4 +- .../my-upg-100-list/poster-wall/index.html | 2 +- .../my-upg-100-list/upg-100-list/index.html | 2 +- .../my-upg-100-list/upg-100-mfe/index.html | 2 +- .../doxa-maps/research-dashboard/index.html | 6 +- .../research-dashboard/research-dashboard.js | 26 +-- .../research-dashboard/index.html | 2 +- public/doxa-maps/template-bundle/index.html | 6 +- .../template-bundle/template-bundle.js | 4 +- .../template-bundle/template-map-a/index.html | 2 +- .../template-bundle/template-map-b/index.html | 2 +- public/index.html | 2 +- vite.config.js | 4 +- 136 files changed, 393 insertions(+), 641 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 316333e8e..4f23b3a52 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,7 +7,7 @@ lookups in [docs/reference.md](./docs/reference.md), depth in ## The mental model — three zones ``` - library/ (@map) app-profiles// internal/ + library/ (@map) plugin-designs// internal/ shared, parameterized ──► your map: its screens ──► the build machine legend · map · controls (.vue) + private colors, (scripts, staging) REUSE, never customize api wiring, components NEVER touch @@ -56,6 +56,6 @@ it (script tag or the generated per-map page). Same-origin vs external-CDN modes ## One-line recap -Copy `app-profiles/template-bundle/`, change exactly four things (folder name, tag in +Copy `plugin-designs/template-bundle/`, change exactly four things (folder name, tag in `index.js`, tag/profile strings in `index.html`, `.vue` filenames), build everything inside your folder from `@map` blocks, `bun run build`, embed the snippet. That's the whole job. diff --git a/README.md b/README.md index c6c3698d3..827ad3d32 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ New to this? Read this table first. You do **not** need to memorize it — just ## What doxa-maps gives you -- **One repository for all your maps.** Each map is an *application profile* in `app-profiles//`. +- **One repository for all your maps.** Each map is an *application profile* in `plugin-designs//`. - **A shared library of reusable, parameterized components** (`library/` — the map, the legend, the controls, the search bar) so your maps stop duplicating code. - **Room for each map's own custom code** — because real maps always have something unique. @@ -197,9 +197,9 @@ JavaScript.* They were right. So here's the honest deal before you start: - `bun run dev` — the dev server (live preview) - `bun run build` — the build step (ship it) - what `node_modules` are (the downloaded dependencies) -3. **You do *not* need to write the code.** Give your agent access to the example maps in `app-profiles/` plus +3. **You do *not* need to write the code.** Give your agent access to the example maps in `plugin-designs/` plus the shared `library/`, describe your map in plain English, and it builds a new map that reuses the library - instead of duplicating spaghetti. The library is for reusable components while the app profiles is for storing custom components that are not reusable, and are specific to that app-profile or map-app. Each map app has custom components that are not reusable and they are not appropriate in the shared library, but if you look at other app-profiles you will see they store their custom components, as well as their main.vue file for the map in the app-profiles folder. + instead of duplicating spaghetti. The library is for reusable components while the app profiles is for storing custom components that are not reusable, and are specific to that app-profile or map-app. Each map app has custom components that are not reusable and they are not appropriate in the shared library, but if you look at other plugin-designs you will see they store their custom components, as well as their main.vue file for the map in the plugin-designs folder. That's the wall. It's small, and everything past it is the maps of your dreams. @@ -216,7 +216,7 @@ bun run dev # the plugin gallery: http://localhost:5173 (or the next free # an auto-generated SPA of every map, grouped by bundle and composition tier, # each deep-linkable; `bun run smoke:gallery` proves every one of them boots -bun run build # the build step: every app-profiles// becomes +bun run build # the build step: every plugin-designs// becomes # public/doxa-maps//.js + a standalone index.html per map bun run serve # preview the BUILT tree (the SPA shell + every embed page) diff --git a/contributing/COLORS.md b/contributing/COLORS.md index 4368ef032..dd3c2ad75 100644 --- a/contributing/COLORS.md +++ b/contributing/COLORS.md @@ -34,7 +34,7 @@ Use this when the color is specific to your map — you want a different shade t shared one, or a brand-new coloring nobody else has. This keeps the shared library clean ("local by default"). -1. In your bundle folder, create `app-profiles//src/colors/.js`. +1. In your bundle folder, create `plugin-designs//src/colors/.js`. (No such folder yet? Just make it. `template-bundle/src/colors/example-mode.js` is a copy-me starting point.) 2. Follow the same contract as a library strategy @@ -80,7 +80,7 @@ loader, the store) references them, so they correctly stay in the library: `adoption`, `engagement`, `language-family`, `prayer-progress`, `religion`. Three more are **research-only** and live in the research bundle's own folder, -`app-profiles/doxa-research-map/src/colors/`: `affinity-block`, `doxa-region`, +`plugin-designs/doxa-research-map/src/colors/`: `affinity-block`, `doxa-region`, `resource`. If you want *your* map to color one of these differently, don't edit the library — drop diff --git a/contributing/README.md b/contributing/README.md index b2ab2dd3f..af9dc9b59 100644 --- a/contributing/README.md +++ b/contributing/README.md @@ -9,7 +9,7 @@ being duplicated. > **Parameterized + genuinely reused by more than one map → the shared library** > (`library/`, alias `@map`). -> **Not reused → it lives in your app-profile folder** (`app-profiles//`). +> **Not reused → it lives in your app-profile folder** (`plugin-designs//`). "**Local by default, promote deliberately.**" When in doubt, keep your change local to your own map; something graduates into the shared library only once a *second* map genuinely @@ -31,6 +31,6 @@ needs it. Full reasoning — the promotion / orphan-audit rule — is in [`COLORS.md`](./COLORS.md) is the beginner deep-dive on the **two places a color strategy can live** — the shared `library/colors/` set vs your bundle's private -`app-profiles//src/colors/` — and how the filename decides override-vs-add. The +`plugin-designs//src/colors/` — and how the filename decides override-vs-add. The strategy **contract** (the required `export default` shape) is in [`../library/colors/README.md`](../library/colors/README.md). diff --git a/contributing/staging-reference-design.html b/contributing/staging-reference-design.html index 46589d34f..f3611fcde 100644 --- a/contributing/staging-reference-design.html +++ b/contributing/staging-reference-design.html @@ -79,7 +79,7 @@ - #stage/#showcase: live gem-framed map tiles, desktop/mobile viewport toggle, lazy-mount + WebGL guard, collapsing header on scroll (mobile) - loads ./staging-main.js - Tabs auto-detect bundles from /app-profiles/*/. --> + Tabs auto-detect bundles from /plugin-designs/*/. --> diff --git a/data/README.md b/data/README.md index 06c958653..10297729b 100644 --- a/data/README.md +++ b/data/README.md @@ -2,15 +2,15 @@ **The law: data lives here; `public/` is generated.** Every folder in `data/` is copied into the served web-root by the build (`internal/generate-tree.mjs`), exactly like the per-profile data in -`app-profiles/*/data/`. You should be able to `rm -rf public/` and get it all back with `bun run build`. +`plugin-designs/*/data/`. You should be able to `rm -rf public/` and get it all back with `bun run build`. | Folder | Served at | What it is | |---|---|---| | `people-groups/` | `/assets/people-groups/` | People-group datasets (CSV/JSON) the maps read at runtime | | `vendor/` | `/assets/vendor/` | Third-party runtime assets (e.g. table libraries) | | `posters/` | `/assets/posters/` | Rendered poster webp set (heavy pipeline output; poster walls fetch these) | -| `tables/` | `/assets/tables/` | The MERGED served table set (its index.json is the merge; per-profile `app-profiles/*/data/tables` are build-time sources) | +| `tables/` | `/assets/tables/` | The MERGED served table set (its index.json is the merge; per-profile `plugin-designs/*/data/tables` are build-time sources) | | `external-bundles/` | not served | Prebuilt map IIFEs vendored from sibling projects (not built here); only the optional `layouts/` shell mounted them, so the build no longer copies them into `public/` | -Don't edit anything under `public/` by hand — change it here (or in `app-profiles/*/data/`, +Don't edit anything under `public/` by hand — change it here (or in `plugin-designs/*/data/`, `layouts/*/assets/`) and rebuild. diff --git a/docs/architecture/app-wrappers.md b/docs/architecture/app-wrappers.md index ad6fd05b9..9e392a443 100644 --- a/docs/architecture/app-wrappers.md +++ b/docs/architecture/app-wrappers.md @@ -3,7 +3,7 @@ > The simple overview is in `layouts/README.md`. This is the full contract for building and wiring > an app wrapper. -Each folder in `layouts/` is an **app wrapper**: a host shell that composes doxa-maps app-profiles +Each folder in `layouts/` is an **app wrapper**: a host shell that composes doxa-maps plugin-designs (the independently-built custom elements) into one application. ## Terminology (deliberate — don't drift) @@ -11,7 +11,7 @@ Each folder in `layouts/` is an **app wrapper**: a host shell that composes doxa | Thing | What it is | |---|---| | **app-profile** | An independently-built, self-contained custom element (a map, a dashboard, a list). **These are the micro-frontends.** | -| **app wrapper** (this folder) | The **host/shell** that mounts app-profiles, provides nav/chrome/routing, and bounds resources. Not itself a micro-frontend — it's the container. | +| **app wrapper** (this folder) | The **host/shell** that mounts plugin-designs, provides nav/chrome/routing, and bounds resources. Not itself a micro-frontend — it's the container. | A wrapper is a *host page*, **not** a regular app-profile. Keeping it a host (rather than a bundle nested inside a bundle) avoids the relative-path/base-URL resolution problem that nesting one diff --git a/docs/architecture/color-strategies.md b/docs/architecture/color-strategies.md index 26f371e33..27d7c61f8 100644 --- a/docs/architecture/color-strategies.md +++ b/docs/architecture/color-strategies.md @@ -53,7 +53,7 @@ operator (`'match'`, `'case'`, `'interpolate'`, …). It feeds `circle-color` in ## Profile-local strategies (override / add without touching the library) `library/colors/` is the **shared** set. A single map that wants its own coloring drops a `.js` -in that bundle's `app-profiles//src/colors/`. At init, `index.js` calls +in that bundle's `plugin-designs//src/colors/`. At init, `index.js` calls `registerStrategies(import.meta.glob('./src/colors/*.js', { eager: true }))`, merging local files **over** the shared set: @@ -62,7 +62,7 @@ in that bundle's `app-profiles//src/colors/`. At init, `index.js` calls Each bundle is its own IIFE, so a local strategy can't leak to another map. This is the "local by default, promote deliberately" seam. See `contributing/COLORS.md` and -`app-profiles/template-bundle/src/colors/` for the copy-me example. +`plugin-designs/template-bundle/src/colors/` for the copy-me example. ## Status / custom modes diff --git a/docs/architecture/file-map.md b/docs/architecture/file-map.md index dc8dc8e74..3c67f38f7 100644 --- a/docs/architecture/file-map.md +++ b/docs/architecture/file-map.md @@ -167,48 +167,48 @@ Generated by a per-file scan. The `@map/…` import path mirrors each `library/` --- -## app-profiles/ — one folder per bundle +## plugin-designs/ — one folder per bundle ### demo-map/ | File | What it is | Agents should know | |---|---|---| -| `app-profiles/demo-map/index.js` | Bundle entry: defines/registers the `` custom element wrapping @map/ProfileLoader. | import.meta.glob calls must live HERE (build-time, relative): `./*.vue` → 'profileModules', `./src/colors/*.js` → registerStrategies. i18n provided as 'appI18n' (custom-element root can't useI18n()). | -| `app-profiles/demo-map/index.html` | Dev/staging page: loads Mapbox GL peer, mounts `` with a profile-config attribute, pulls ./index.js as Vite entry. | %VITE_MAPBOX_TOKEN% replaced by Vite from gitignored .env; sets window.DOXA_TOKEN + MAP_APP_BASE_URL; Mapbox GL must load before the bundle script. | -| `app-profiles/demo-map/demo-map.vue` | Demo profile: two-tab map (religion coloring vs all-purple pins) composed from @map composables + a local single-color strategy. | Relies on ProfileLoader injections; tab colorStrategy IDs must exist in the registry; layer name 'language-family-pins' is hardcoded for tab repaints; legend duck-types the strategy shape. | -| `app-profiles/demo-map/src/colors/colors.json` | Static hex palette (pins, polygons, fallback) — code-free edit surface. | Edit hex strings only; per-value logic goes in a strategy .js in the same folder. | -| `app-profiles/demo-map/src/colors/single-color.js` | Local strategy painting every pin one constant purple. | Auto-registered via the src/colors glob — filename becomes the mode key (single-color → singleColor); keep the buildColorExpression alias and default `name`; applyColor must return a Mapbox expression array. | +| `plugin-designs/demo-map/index.js` | Bundle entry: defines/registers the `` custom element wrapping @map/ProfileLoader. | import.meta.glob calls must live HERE (build-time, relative): `./*.vue` → 'profileModules', `./src/colors/*.js` → registerStrategies. i18n provided as 'appI18n' (custom-element root can't useI18n()). | +| `plugin-designs/demo-map/index.html` | Dev/staging page: loads Mapbox GL peer, mounts `` with a profile-config attribute, pulls ./index.js as Vite entry. | %VITE_MAPBOX_TOKEN% replaced by Vite from gitignored .env; sets window.DOXA_TOKEN + MAP_APP_BASE_URL; Mapbox GL must load before the bundle script. | +| `plugin-designs/demo-map/demo-map.vue` | Demo profile: two-tab map (religion coloring vs all-purple pins) composed from @map composables + a local single-color strategy. | Relies on ProfileLoader injections; tab colorStrategy IDs must exist in the registry; layer name 'language-family-pins' is hardcoded for tab repaints; legend duck-types the strategy shape. | +| `plugin-designs/demo-map/src/colors/colors.json` | Static hex palette (pins, polygons, fallback) — code-free edit surface. | Edit hex strings only; per-value logic goes in a strategy .js in the same folder. | +| `plugin-designs/demo-map/src/colors/single-color.js` | Local strategy painting every pin one constant purple. | Auto-registered via the src/colors glob — filename becomes the mode key (single-color → singleColor); keep the buildColorExpression alias and default `name`; applyColor must return a Mapbox expression array. | ### doxa-research-map/ | File | What it is | Agents should know | |---|---|---| -| `app-profiles/doxa-research-map/index.js` | Bundle entry: globs profiles + colors, registers strategies, sets up the RTL plugin, defines ``. | Globs are relative to THIS file — profile .vue at bundle root, strategies in src/colors/, or they silently don't register. Deliberately does NOT register legacy `` (owned by doxa-simple-map; one-shot customElements.define). | -| `app-profiles/doxa-research-map/index.html` | Staging showcase: gem-framed instances from a flat DOXA_SLOTS array, profile tabs, overview grid. | Slot `profile` must match a .vue filename at bundle root; profile + tk (+ ?locale=) auto-injected into config; ./index.js script tag rewritten to the IIFE at build. | -| `app-profiles/doxa-research-map/research-map.vue` | The research-map profile (~2100 lines): 7-tab map (prayer/adoption/engagement/people-groups/regions/languages/religions) with SemanticTreeLegend, geocoder, filters, detail panel, lazy poster/packet export. | Tabs are hardcoded in ALL_TABS/FEATURES (NOT profile-config); config arrives only via injections; maxZoom pinned to 22 deliberately; heavy deps (poster, packet, clustering, detail panel, mobile legend) are lazy-loaded — keep them off the boot path. | -| `app-profiles/doxa-research-map/src/colors/affinity-block.js` | Strategy mapping ROP1 affinity-block codes (A001–A017) to names and pin colors. | ROP1 code is the primary key (name lookups are lossy legacy); reads properties.affinityBlock falling back to `_raw.ROP1`; keep NAMES and PALETTE keys in sync; A016 intentionally absent. | -| `app-profiles/doxa-research-map/src/colors/doxa-region.js` | Strategy coloring pins by DOXA/WAGF region; SLUG_PALETTE is authoritative. | Color by slug, never localized display labels (break in non-EN locales); uses @map/utils/geoUtils generateColorFromString as fallback. | -| `app-profiles/doxa-research-map/src/colors/resource.js` | Binary green/black coloring by gospel-resource availability + per-resource palette for the legend. | Default coloring is intentionally binary despite the per-resource PALETTE; resource values must literally equal 'Available' (properties or _raw CSV columns via getCSVColumn); legacy aliases kept for compat. | +| `plugin-designs/doxa-research-map/index.js` | Bundle entry: globs profiles + colors, registers strategies, sets up the RTL plugin, defines ``. | Globs are relative to THIS file — profile .vue at bundle root, strategies in src/colors/, or they silently don't register. Deliberately does NOT register legacy `` (owned by doxa-simple-map; one-shot customElements.define). | +| `plugin-designs/doxa-research-map/index.html` | Staging showcase: gem-framed instances from a flat DOXA_SLOTS array, profile tabs, overview grid. | Slot `profile` must match a .vue filename at bundle root; profile + tk (+ ?locale=) auto-injected into config; ./index.js script tag rewritten to the IIFE at build. | +| `plugin-designs/doxa-research-map/research-map.vue` | The research-map profile (~2100 lines): 7-tab map (prayer/adoption/engagement/people-groups/regions/languages/religions) with SemanticTreeLegend, geocoder, filters, detail panel, lazy poster/packet export. | Tabs are hardcoded in ALL_TABS/FEATURES (NOT profile-config); config arrives only via injections; maxZoom pinned to 22 deliberately; heavy deps (poster, packet, clustering, detail panel, mobile legend) are lazy-loaded — keep them off the boot path. | +| `plugin-designs/doxa-research-map/src/colors/affinity-block.js` | Strategy mapping ROP1 affinity-block codes (A001–A017) to names and pin colors. | ROP1 code is the primary key (name lookups are lossy legacy); reads properties.affinityBlock falling back to `_raw.ROP1`; keep NAMES and PALETTE keys in sync; A016 intentionally absent. | +| `plugin-designs/doxa-research-map/src/colors/doxa-region.js` | Strategy coloring pins by DOXA/WAGF region; SLUG_PALETTE is authoritative. | Color by slug, never localized display labels (break in non-EN locales); uses @map/utils/geoUtils generateColorFromString as fallback. | +| `plugin-designs/doxa-research-map/src/colors/resource.js` | Binary green/black coloring by gospel-resource availability + per-resource palette for the legend. | Default coloring is intentionally binary despite the per-resource PALETTE; resource values must literally equal 'Available' (properties or _raw CSV columns via getCSVColumn); legacy aliases kept for compat. | ### doxa-simple-map/ | File | What it is | Agents should know | |---|---|---| -| `app-profiles/doxa-simple-map/index.js` | Bundle entry registering `` (legacy) AND `` mounting ProfileLoader with per-instance Pinia/i18n. | Q3 contract: the bundle runs import.meta.glob('./*.vue') itself and provides 'profileModules' (ProfileLoader can't glob across @map). defineCustomElement called twice on purpose (one class per tag); RTL plugin registered once. | -| `app-profiles/doxa-simple-map/index.html` | Staging showcase: gem-framed `` slots (Engagement/Prayer/Adoption) with profile tabs and overview grid. | Edit only DOXA_SLOTS; `profile` must match a bundle-root .vue; ?locale= forwarded into every config; build rewrites the module script to ../../app/doxa-simple-map.iife.js. | -| `app-profiles/doxa-simple-map/doxa-simple-map.vue` | Tabbed (prayer/engagement/adoption) single-map profile wiring library composables, legend, geocoder, and strategies into one Shadow-DOM-safe embed. | Behavior gated by in-file FEATURES flags (geocoder/hamburger/overlapDensity/activeOverlayEmphasis OFF here); tabs come from injected profileConfig; SHARE_EMBED_BY_ID hardcodes instance-page URLs — renaming instances in simple-map.instances.json breaks share links. | -| `app-profiles/doxa-simple-map/simple-map.instances.json` | Instance manifest: build emits N preset standalone embed pages (prayer/engagement/adoption) from the one parameterized profile. | Build-consumed data, not runtime code: each instance becomes `doxa-simple-map//index.html`; instances SPLIT one profile into many render targets; colorStrategy/legend/popup values must match registered strategy names. | +| `plugin-designs/doxa-simple-map/index.js` | Bundle entry registering `` (legacy) AND `` mounting ProfileLoader with per-instance Pinia/i18n. | Q3 contract: the bundle runs import.meta.glob('./*.vue') itself and provides 'profileModules' (ProfileLoader can't glob across @map). defineCustomElement called twice on purpose (one class per tag); RTL plugin registered once. | +| `plugin-designs/doxa-simple-map/index.html` | Staging showcase: gem-framed `` slots (Engagement/Prayer/Adoption) with profile tabs and overview grid. | Edit only DOXA_SLOTS; `profile` must match a bundle-root .vue; ?locale= forwarded into every config; build rewrites the module script to ../../app/doxa-simple-map.iife.js. | +| `plugin-designs/doxa-simple-map/doxa-simple-map.vue` | Tabbed (prayer/engagement/adoption) single-map profile wiring library composables, legend, geocoder, and strategies into one Shadow-DOM-safe embed. | Behavior gated by in-file FEATURES flags (geocoder/hamburger/overlapDensity/activeOverlayEmphasis OFF here); tabs come from injected profileConfig; SHARE_EMBED_BY_ID hardcodes instance-page URLs — renaming instances in simple-map.instances.json breaks share links. | +| `plugin-designs/doxa-simple-map/simple-map.instances.json` | Instance manifest: build emits N preset standalone embed pages (prayer/engagement/adoption) from the one parameterized profile. | Build-consumed data, not runtime code: each instance becomes `doxa-simple-map//index.html`; instances SPLIT one profile into many render targets; colorStrategy/legend/popup values must match registered strategy names. | ### template-bundle/ (copy-me starter) | File | What it is | Agents should know | |---|---|---| -| `app-profiles/template-bundle/index.js` | Copy-me entry: globs flat .vue profiles, registers private strategies, defines ``. | Globs are position-bound: `./*.vue` flat (filename = profile name) and `./src/colors/*.js` (colors.json ignored by the glob); tag name = bundle folder name. | -| `app-profiles/template-bundle/index.html` | Staging page rendering both template profiles side-by-side. | The profile-config `"profile"` field selects which bundle-root .vue renders; copying this bundle means updating tag names and profile strings. | -| `app-profiles/template-bundle/template-map-a.vue` | Minimal example profile "A" — colored placeholder, no Mapbox/data, demonstrates profile-config injection + sandbox color JSON. | Filename minus .vue IS the profile name; must sit at bundle root; sandbox imports are relative (./src/...) — @map alias is library-only. | -| `app-profiles/template-bundle/template-map-b.vue` | Minimal example profile "B" — shows multi-profile bundles; both profiles compile into one bundle JS. | Host selects it via profile-config `{"profile":"template-map-b"}`. | -| `app-profiles/template-bundle/src/colors/colors.json` | Plain-values hex palette a non-programmer edits. | Values-only; logic goes in a strategy .js beside it; imported relatively from the bundle-root .vue. | -| `app-profiles/template-bundle/src/colors/example-mode.js` | Copy-me template for a bundle-private strategy (two-bucket example on peoplePraying). | Filename IS the mode key (kebab → camelCase); matching a library mode overrides it for this bundle only; `name` in the default export is required or the tab/legend label is blank. | +| `plugin-designs/template-bundle/index.js` | Copy-me entry: globs flat .vue profiles, registers private strategies, defines ``. | Globs are position-bound: `./*.vue` flat (filename = profile name) and `./src/colors/*.js` (colors.json ignored by the glob); tag name = bundle folder name. | +| `plugin-designs/template-bundle/index.html` | Staging page rendering both template profiles side-by-side. | The profile-config `"profile"` field selects which bundle-root .vue renders; copying this bundle means updating tag names and profile strings. | +| `plugin-designs/template-bundle/template-map-a.vue` | Minimal example profile "A" — colored placeholder, no Mapbox/data, demonstrates profile-config injection + sandbox color JSON. | Filename minus .vue IS the profile name; must sit at bundle root; sandbox imports are relative (./src/...) — @map alias is library-only. | +| `plugin-designs/template-bundle/template-map-b.vue` | Minimal example profile "B" — shows multi-profile bundles; both profiles compile into one bundle JS. | Host selects it via profile-config `{"profile":"template-map-b"}`. | +| `plugin-designs/template-bundle/src/colors/colors.json` | Plain-values hex palette a non-programmer edits. | Values-only; logic goes in a strategy .js beside it; imported relatively from the bundle-root .vue. | +| `plugin-designs/template-bundle/src/colors/example-mode.js` | Copy-me template for a bundle-private strategy (two-bucket example on peoplePraying). | Filename IS the mode key (kebab → camelCase); matching a library mode overrides it for this bundle only; `name` in the default export is required or the tab/legend label is blank. | --- @@ -217,7 +217,7 @@ Generated by a per-file scan. The `@map/…` import path mirrors each `library/` | File | What it is | Agents should know | |---|---|---| | `internal/clean.mjs` | Pre-build script that deletes/recreates the output tree `public//`. | Reads the tree name from vite.doxa-maps-build-config.json (fallback "doxa-maps"); must run BEFORE per-bundle vite builds. | -| `internal/generate-tree.mjs` | Post-vite step that scans app-profiles/ and writes the self-contained doxa-maps/ output tree (manifest.json, staging pages, standalone profile/instance pages) and copies to configured destinations. | Runs AFTER per-bundle builds (expects the IIFE at `public///.js`); discovery is convention-driven (bundle = non-underscore dir with .vue at ROOT — profiles moved into src/ silently vanish); all URLs stay relative unless embedBaseUrl is set; inlines staging-forge.{css,js}. | +| `internal/generate-tree.mjs` | Post-vite step that scans plugin-designs/ and writes the self-contained doxa-maps/ output tree (manifest.json, staging pages, standalone profile/instance pages) and copies to configured destinations. | Runs AFTER per-bundle builds (expects the IIFE at `public///.js`); discovery is convention-driven (bundle = non-underscore dir with .vue at ROOT — profiles moved into src/ silently vanish); all URLs stay relative unless embedBaseUrl is set; inlines staging-forge.{css,js}. | | `internal/generate-library-index.mjs` | Scans live library/ code and regenerates docs/reference-library-index.md (one-line import catalog). | Output is DERIVED — never hand-edit the index; rerun after any library/ change; descriptions come from each file's first header-comment line. | | `internal/drop-portability-check.mjs` | CLI gate (`check:drop`): scans built .html/.json for root-relative refs and fails the build if any would break subpath/Drop hosting. | Defaults treeDir to `public/` from the build config — build first; only checks .html/.json and a fixed manifest key list (iframeUrl\|scriptUrl\|url\|embedUrl\|output) — new URL keys need adding here. | | `internal/inline-parse-check.mjs` | Post-build verifier (`check:parse`): syntax-checks every inline classic `` in snippets is exactly the bug class it catches. | @@ -227,7 +227,7 @@ Generated by a per-file scan. The `@map/…` import path mirrors each `library/` | `internal/staging-reference-v2.html` | Hand-authored reference mockup of the staging-root tree page (nested details, lazy iframes) that generate-tree.mjs's template replicates. | Not served or built — spec-by-example; rules encoded: inline manifest (file:// safe), lazy iframe src on toggle, tk never baked, normal-flow header. | | `library/scripts/research/` | Table + resource builders shared by cities-internet, gospel-resources and key-cities. In `library/` because more than one profile consumes the output. | | `library/scripts/posters/` | Poster and preview generator shared by country-posters and my-upg-100-list. Its `out/` tree is render output and is gitignored; what ships is the exported subset in `public/assets/`. | -| `app-profiles/gospel-resources/scripts/` | Link and click-sheet verifiers. Single-profile, so they live with the profile rather than in the library. | +| `plugin-designs/gospel-resources/scripts/` | Link and click-sheet verifiers. Single-profile, so they live with the profile rather than in the library. | | `internal/write-token.mjs` | Writes `public/api/maps/token.json` from the `MAPBOX_TOKEN` environment variable. Refuses an `sk.*`; writes nothing when unset. | --- @@ -236,9 +236,9 @@ Generated by a per-file scan. The `@map/…` import path mirrors each `library/` | File | What it is | Agents should know | |---|---|---| -| `vite.config.js` | Vite config building one IIFE bundle per invocation (BUNDLE env var) from auto-discovered app-profiles/; serves all bundles in dev. | Build requires `BUNDLE=` (`npm run build` loops all); bundle discovery needs BOTH index.html AND index.js at profile root (`_`/`.` dirs skipped); `@map` alias → library/; jspdf externalized to an esm.sh CDN URL in builds; output name from vite.doxa-maps-build-config.json. | +| `vite.config.js` | Vite config building one IIFE bundle per invocation (BUNDLE env var) from auto-discovered plugin-designs/; serves all bundles in dev. | Build requires `BUNDLE=` (`npm run build` loops all); bundle discovery needs BOTH index.html AND index.js at profile root (`_`/`.` dirs skipped); `@map` alias → library/; jspdf externalized to an esm.sh CDN URL in builds; output name from vite.doxa-maps-build-config.json. | | `vite.doxa-maps-build-config.json` | The single committed build-config: output tree name ("doxa-maps"), extra copy destinations (paths), embedBaseUrl, Mapbox tokenUrl/token. | Edit this ONE file then `bun run build`; empty embedBaseUrl keeps manifest URLs tree-relative (drop-portability gate); token ladder: per-map prop > ?tk= > baked token > window.MAP_TOKEN_URL > tokenUrl > same-origin /api/maps/token. tokenUrl is a URL, not a secret. | -| `package.json` | Root npm/bun manifest: scripts (dev, build, build:bundle, clean, tree, check:drop, check:parse) + deps (vue 3, pinia, vue-i18n, topojson-client, world-atlas, jspdf; vite 5 toolchain). | `build` iterates app-profiles/*/ setting BUNDLE per invocation — adding a folder auto-builds; internal/ scripts run as part of build (check:drop currently invoked twice, likely unintentional). Bun-first toolchain. | +| `package.json` | Root npm/bun manifest: scripts (dev, build, build:bundle, clean, tree, check:drop, check:parse) + deps (vue 3, pinia, vue-i18n, topojson-client, world-atlas, jspdf; vite 5 toolchain). | `build` iterates plugin-designs/*/ setting BUNDLE per invocation — adding a folder auto-builds; internal/ scripts run as part of build (check:drop currently invoked twice, likely unintentional). Bun-first toolchain. | | `index.html` | Root staging page ("Embeddable JAMstack Maps") listing every bundle as tabs and lazy-mounting each bundle's maps in live frames; logic in /internal/staging.js. | Loads mapbox-gl v3.24.0 + geocoder globally ONCE so custom elements share window.mapboxgl in the same realm; pinned CDN versions must match library expectations. | diff --git a/docs/architecture/i18n.md b/docs/architecture/i18n.md index 99ecea949..8dbb5bb5f 100644 --- a/docs/architecture/i18n.md +++ b/docs/architecture/i18n.md @@ -61,7 +61,7 @@ i18n/ ## How it integrates with the component library -The app-profile entry (`app-profiles//index.js`) does `app.use(createAppI18n())` alongside +The app-profile entry (`plugin-designs//index.js`) does `app.use(createAppI18n())` alongside `createPinia()`, so every component reaches strings through the standard `$t('area.key')` / `useI18n()`. Initial locale comes from `detectLocale()` (the host page's ``); the fallback chain is ` → en`. Because area files are namespaced by filename, a component diff --git a/docs/architecture/patterns-and-provenance.md b/docs/architecture/patterns-and-provenance.md index 04f5c30ae..b21e40ae0 100644 --- a/docs/architecture/patterns-and-provenance.md +++ b/docs/architecture/patterns-and-provenance.md @@ -29,7 +29,7 @@ A single bundle can present **several different maps**. There are **two ways** t do it, and they solve different problems. Neither is "more correct" — pick the one that matches *why* your maps differ. -> **Vocabulary.** A **bundle** is one folder under `app-profiles//` → one +> **Vocabulary.** A **bundle** is one folder under `plugin-designs//` → one > `.js` script. A **profile** is a `.vue` file at that folder's root, > discovered by `import.meta.glob('./*.vue')` in the bundle's `index.js`. The host > picks which profile to mount with the `profile` field of `profile-config`; the diff --git a/docs/architecture/poster-generation.md b/docs/architecture/poster-generation.md index 62ca81514..90c9f6ef8 100644 --- a/docs/architecture/poster-generation.md +++ b/docs/architecture/poster-generation.md @@ -28,7 +28,7 @@ The full pipeline, in order: posters -> posters:shoot -> (convert to WebP previews) -> bun run build ``` -The WebP step feeds `app-profiles/my-upg-100-list/assets/previews/`, which is what the poster wall +The WebP step feeds `plugin-designs/my-upg-100-list/assets/previews/`, which is what the poster wall shows in the app. **A template change is not visible in the app until previews are regenerated and the app is rebuilt.** diff --git a/docs/explanation.md b/docs/explanation.md index 34bf1caba..e20dc41f4 100644 --- a/docs/explanation.md +++ b/docs/explanation.md @@ -1,6 +1,6 @@ # What doxa-maps is -A portable bundler for embeddable maps. One shared `library/`, many `app-profiles//` +A portable bundler for embeddable maps. One shared `library/`, many `plugin-designs//` folders; one build turns each map into a self-contained web component. **The one idea worth knowing:** each map builds to a standard web component, so it drops into diff --git a/docs/how-to.md b/docs/how-to.md index 5dc0e9d70..1a832deb7 100644 --- a/docs/how-to.md +++ b/docs/how-to.md @@ -5,7 +5,7 @@ first, then use this page to look up a task. For contracts and field tables, see **[reference.md](./reference.md)**. The three zones govern everything: **reuse `library/` (`@map`), build in -`app-profiles//`, never touch `internal/`.** +`plugin-designs//`, never touch `internal/`.** **Pick your task:** [embed on a site](#embed-a-map-on-a-partner-site) · [link/iframe directly](#link-or-iframe-a-map-directly-zero-effort-embed) · @@ -68,14 +68,14 @@ copy-paste ready. Design details: [architecture/embeddability.md](./architecture ## Add a new map profile The fastest path is **copy `template-bundle` and rename it.** A bundle is one folder under -`app-profiles/`; its name becomes the bundle name and the output filename. Don't prefix it +`plugin-designs/`; its name becomes the bundle name and the output filename. Don't prefix it with `_` or `.` — those are skipped by the build. 1. **Copy the template.** ```bash - cp -r app-profiles/template-bundle app-profiles/my-new-map + cp -r plugin-designs/template-bundle plugin-designs/my-new-map ``` -2. **Rename the element tag.** In `app-profiles/my-new-map/index.js`, change the +2. **Rename the element tag.** In `plugin-designs/my-new-map/index.js`, change the `customElements.define('…', …)` tag to your map's tag. The entry must glob the bundle's flat profiles and hand them to the loader — it already does this; keep the shape: ```js @@ -186,7 +186,7 @@ auto-discovers every `.js` in that folder, so there's no registration step deliberate — this changes *every* map using that mode. **Path B — a color private to your map.** Create -`app-profiles//src/colors/.js` (copy `template-bundle/src/colors/example-mode.js` +`plugin-designs//src/colors/.js` (copy `template-bundle/src/colors/example-mode.js` as a starter). Your bundle's `index.js` runs `registerStrategies(import.meta.glob('./src/colors/*.js', { eager: true }))` at startup, merging your local files **over** the shared set. The **filename** derives the mode key (`language-family.js` → `languageFamily`): @@ -328,10 +328,10 @@ URL restrictions at Mapbox, not by hiding it. If you point an AI agent at this repo, paste this first, before it reads any code: > This is the doxa-maps bundler. Three zones: `library/` (`@map`) = reusable shared blocks — -> **reuse via the `@map/…` alias, never edit**. `app-profiles//` = the sandbox for **one** +> **reuse via the `@map/…` alias, never edit**. `plugin-designs//` = the sandbox for **one** > map — **build everything here**. `internal/` = the build machine — **never touch it**. -> To build a map: copy `app-profiles/template-bundle/`, read its README, pick reusable pieces -> from `docs/reference.md`, then build the map's own code inside `app-profiles//`. +> To build a map: copy `plugin-designs/template-bundle/`, read its README, pick reusable pieces +> from `docs/reference.md`, then build the map's own code inside `plugin-designs//`. > Change colors by editing **values** in that map's `src/colors/` folder — not logic in > `library/`. Then run `bun run build`. If a change seems to belong in `library/`, stop and > ask — that's an expert move that can break every other map. diff --git a/docs/reference.md b/docs/reference.md index 0790ee970..7e930c57f 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -15,18 +15,18 @@ bundle is a single self-contained `.js` file that registers a custom HTML elemen | Zone | Path | Alias | License | You edit it… | |---|---|---|---|---| | **Shared library** | `library/` | `@map` | Apache-2.0 | …once; the fix reaches every bundle on next build | -| **Your app-profile** | `app-profiles//` | — | yours | …freely; nothing here leaks to other maps | +| **Your app-profile** | `plugin-designs//` | — | yours | …freely; nothing here leaks to other maps | | **The build machine** | `internal/` | — | Apache-2.0 | …never — it's the bundler's own scripts | **Terminology.** "Profile" is used at two levels: a **bundle / app-profile** is a folder under -`app-profiles/`; a **profile screen** is one `.vue` file sitting **flat** in that folder (no +`plugin-designs/`; a **profile screen** is one `.vue` file sitting **flat** in that folder (no `profiles/` subfolder — the entry globs `./*.vue`). A bundle can hold several screens; the host picks which to render with the `profile` field of `profile-config`. **Two words first.** An **element** is a custom HTML tag this repo invents, such as ``: the browser learns it once, then you can paste it into any page as often as you like; each pasted tag is one live copy, an *instance* (people also say "web component"). An **app profile** (this repo also says *mounting file*) is one -flat `.vue` in `app-profiles//` that defines a whole map app — its main component; the settings on the +flat `.vue` in `plugin-designs//` that defines a whole map app — its main component; the settings on the tag (`profile-config`) name which app profile to load and the shared `ProfileLoader` picks it. In class-and-object terms: the element class is the class, each tag is an instance, the app profile is the screen that instance loads. Most repos need one such file per app; doxa-maps builds hundreds of maps from one shared `library/`, so every map @@ -70,7 +70,7 @@ doxa-maps/ │ ├── stores/ ← Pinia: dataStore, mapStore, uiStore (per-instance) │ └── utils/ ← pure helpers (geoUtils, clustering) │ -├── app-profiles/ ← ALL bundles live here (each subfolder = 1 bundle) +├── plugin-designs/ ← ALL bundles live here (each subfolder = 1 bundle) │ ├── / │ │ ├── index.js ← bundle ENTRY — globs ./*.vue, registers the web component │ │ ├── index.html ← Vite html entry = the dev STAGING page @@ -86,7 +86,7 @@ doxa-maps/ │ ├── index.html the app shell │ ├── doxa-maps// built bundles + manifest.json + staging HTML │ └── assets/ posters & data the maps fetch at runtime -├── vite.config.js ← multi-entry: discovers app-profiles// folders +├── vite.config.js ← multi-entry: discovers plugin-designs// folders └── vite.doxa-maps-build-config.json ← output tree name + copy destinations + token settings ``` @@ -151,12 +151,12 @@ each bundle registers a **distinct** tag. | Script | What it does | |---|---| -| `bun run dev` | Vite dev server + HMR (default `http://localhost:5173`, or the next free port — read the terminal). `/` is the **plugin gallery** of every element — `internal/generate-gallery.mjs` in mode `dev` over `internal/plugin-gallery/{discover,dev-plugin}.mjs`; every bundle is also served at its own html path. Dev-only routes: `/__plugin-gallery/manifest.json` (the manifest-shaped model the page fetches), `/__plugin-gallery/registry.json` (the discovery registry the smoke tools read), `/assets/*` (served from `public/assets/`, else `app-profiles/*/data/`). **Writes nothing to `public/`** — no HMR across that boundary. | +| `bun run dev` | Vite dev server + HMR (default `http://localhost:5173`, or the next free port — read the terminal). `/` is the **plugin gallery** of every element — `internal/generate-gallery.mjs` in mode `dev` over `internal/plugin-gallery/{discover,dev-plugin}.mjs`; every bundle is also served at its own html path. Dev-only routes: `/__plugin-gallery/manifest.json` (the manifest-shaped model the page fetches), `/__plugin-gallery/registry.json` (the discovery registry the smoke tools read), `/assets/*` (served from `public/assets/`, else `plugin-designs/*/data/`). **Writes nothing to `public/`** — no HMR across that boundary. | | `bun run smoke:gallery` | Opens every element of the plugin gallery by deep link in Firefox and proves it boots: element defined, shadow DOM populated, no error card, a WebGL canvas for map profiles (checked through nested shadow roots), no HTML-for-JSON data responses. `--json ` writes the evidence; exit 1 on any failure. The pre-handoff gate. | | `bun run smoke:share` | The share round-trip gate (Firefox): opens a host page (`--plugin-gallery` = every canvas element of the dev plugin gallery; `--url --tag ` = the website or any CDN page), clicks Share, reads the URL, fetches it, reopens it in a fresh page and asserts the SAME custom element mounts with a WebGL canvas. `--json `; exit 1 on any failure — proves REQ-1 ("links work wherever the tree is hosted"). | | `bun run check:public-hygiene [] [--strict]` | Proves a served folder is fit to publish: scans for local paths, internal folder names, dev-server URLs, JWT-shaped tokens, `.env`/token files, source maps, documentation and staging/preview pages, and prints an inventory (bundles, leaf pages, assets by folder). Under `--strict` any root entry other than `index.html`, `assets/` and `doxa-maps/` is a finding ("outside the drop-in contract"). Exit 1 on a finding. Run it on the website's drop-in before any public push. | | `bun run sync:dropin [--from-config \| --only a,b] [--prune]` / `bun run deploy:dev` | Syncs the built `public/` into a host checkout (e.g. the website's `public/embed/doxa-maps-build/`). **The drop-in contract** lives in ONE place — `vite.doxa-maps-build-config.json` → `dropin.include`, exactly three root entries: `index.html` (the built plugin gallery — the root page generated by `internal/generate-gallery.mjs`, listing every bundle/profile/instance and mounting the chosen one), `assets/` (data) and `doxa-maps/` (the bundle folders + `manifest.json`). `--from-config` copies only those, `--prune` removes everything else in the destination, so a host carries exactly the served web-root and nothing else at its root. `dropin.exclude` (same file) lists built files inside the allowlist that no page fetches — the full people-groups CSV, the tables only internal scripts read, the table index; they stay in `public/` for the scripts, are skipped by `--from-config` (counted as "excluded (not shipped)") and removed from the destination if a copy is there. Without an allowlist it is a plain full mirror. Never copies credentials, docs or staging pages; `--protect a,b` keeps host-owned files; `--dry-run` first; prints a copied/unchanged/deleted/pruned/excluded summary and the resulting top-level tree. `deploy:dev` = `build` → sync `--from-config --prune` into the website (`DOXA_DROPIN_DEST` overrides the path) — the only sanctioned way a map changes on the site; then commit in both repos. | -| `bun run build` | `clean` → one `vite build` per `app-profiles/*/` folder → `tree` → `check:drop` → `check:parse` → `verify` → `gallery` (writes `public/index.html`, the built plugin gallery, from the manifest). | +| `bun run build` | `clean` → one `vite build` per `plugin-designs/*/` folder → `tree` → `check:drop` → `check:parse` → `verify` → `gallery` (writes `public/index.html`, the built plugin gallery, from the manifest). | | `bun run build:bundle` | Builds ONE bundle (needs `BUNDLE=`). | | `bun run build:watch` | Rebuilds into `public/` on every change (integration loop). | | `bun run clean` | Wipes + recreates the `public//` output tree. | @@ -172,13 +172,13 @@ the phone layout and the baked fallback. Mode `build` (`bun run gallery`) render `public/doxa-maps/manifest.json` into `public/index.html` — the root page of the served web-root and, through the drop-in, the website's `/doxa-maps` page. Mode `dev` is what `bun run dev` serves at `/`: the same page over `galleryModel(discoverSpaRegistry())` (`internal/plugin-gallery/dev-plugin.mjs`), differing -only in the loader (it imports `/app-profiles//index.js` instead of adding the built IIFE), the live +only in the loader (it imports `/plugin-designs//index.js` instead of adding the built IIFE), the live model route (`/__plugin-gallery/manifest.json`), the `/assets/*` dev route and the composability ladder's "?" popovers in the header — kept out of the built page to keep it lean. Nothing about the gallery is written twice. **How a build works.** `vite.config.js` is multi-entry by discovery: at build time it -registers every `app-profiles/` subfolder that (a) doesn't start with `_`/`.` and (b) has +registers every `plugin-designs/` subfolder that (a) doesn't start with `_`/`.` and (b) has **both** `index.html` and `index.js`. One `vite build` builds **exactly one** bundle, selected by `BUNDLE=`, so the output is a self-contained **IIFE** (`inlineDynamicImports: true`). `bun run build` loops every folder. Each bundle only bundles what its screens import, so sizes @@ -304,13 +304,13 @@ The **Semantic Tree Legend** component (a reusable hierarchical legend) is docum | I want to… | Do this | |---|---| -| Add a new map | copy `app-profiles/template-bundle/` → rename folder + tag in `index.js` | +| Add a new map | copy `plugin-designs/template-bundle/` → rename folder + tag in `index.js` | | Add a screen to an existing bundle | drop a `.vue` flat in that bundle's folder, request it via `profile-config.profile` | | Fix a legend / control for ALL maps | edit it once in `library/` (`@map`); rebuild bundles | | Add a color strategy | new file in `@map/colors/` (shared) or the bundle's `src/colors/` (map-private) | | Build everything | `bun run build` → `public/doxa-maps//.js` | | Build just one | `BUNDLE= bun run build:bundle` | -| Preview in isolation | `bun run dev` → `http://localhost:5173/app-profiles//index.html` | +| Preview in isolation | `bun run dev` → `http://localhost:5173/plugin-designs//index.html` | | See a change on the served site | `bun run build`, then refresh (NOT `dev`) | | Repoint the API without a rebuild | set `window.MAP_APP_API_URL` before the bundle loads | | Repoint the Mapbox token | pass `tk` in `profile-config`, or set `window.MAP_TOKEN_URL` / build-config `tokenUrl` | diff --git a/docs/tutorial.md b/docs/tutorial.md index 9e50a1d65..118ab32fd 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -72,7 +72,7 @@ and the ocean is already sand. No reload, no rebuild. That's HMR, and that's the ### Why staging is genuinely magic - **It finds every map by itself.** Staging auto-detects your maps by looking in the - `app-profiles/` folder. Add a folder or a `.vue` file and it appears. There is no roster to + `plugin-designs/` folder. Add a folder or a `.vue` file and it appears. There is no roster to maintain, so you can never "forget to add a map to the index" — the folder *is* the list. - **Your changes appear instantly (HMR).** Save, and the page updates in place. The feeling is less like programming and more like sculpting: nudge, watch, nudge again. @@ -89,10 +89,10 @@ safe from every other map. | Zone | What it is | Can you edit it? | |---|---|---| | **`library/`** (`@map`) | Reusable, shared building blocks — map, legends, toolbars, colors, data. You **reuse** these via the `@map/…` import alias. | **No — reuse only.** A change here can affect every map. | -| **`app-profiles//`** | Your **sandbox**. Your one map lives here: its screens, colors, API wiring. | **Yes — build anything.** Nothing here leaks to another map. | +| **`plugin-designs//`** | Your **sandbox**. Your one map lives here: its screens, colors, API wiring. | **Yes — build anything.** Nothing here leaks to another map. | | **`internal/`** | The bundler's own build machinery. | **No — never.** It's the machine that builds the bundles. | -One line: **reuse `library/`, build in `app-profiles//`, never touch `internal/`.** +One line: **reuse `library/`, build in `plugin-designs//`, never touch `internal/`.** --- @@ -101,14 +101,14 @@ One line: **reuse `library/`, build in `app-profiles//`, never touch ` A new map is a **copy of a great starter, renamed** — you never start from a blank page. > **One map = one file.** A `.vue` file living **flat** in its own folder under -> `app-profiles/`. The folder name is your map's name. +> `plugin-designs/`. The folder name is your map's name. -Inside `app-profiles/` there's a folder called **`template-bundle/`** — a complete, working +Inside `plugin-designs/` there's a folder called **`template-bundle/`** — a complete, working example built to be copied. You don't study it; you copy it. **The plain-English way (what you'll actually do)** — tell your agent: -> *"Copy `app-profiles/template-bundle/` to a new folder called `app-profiles/harvest-map/`, +> *"Copy `plugin-designs/template-bundle/` to a new folder called `plugin-designs/harvest-map/`, > rename the inside files, then make it a map of Southeast Asia shaded by population, with a > blue-to-orange legend on the right and labels in English and Thai."* @@ -118,10 +118,10 @@ change. Conversation in, map out. **The by-hand way (if you want to see what happened):** ```bash -cp -r app-profiles/template-bundle app-profiles/harvest-map # 1. copy the starter +cp -r plugin-designs/template-bundle plugin-designs/harvest-map # 1. copy the starter # 2. change exactly four things: the folder name, the tag in index.js, the tag/profile # strings in index.html, and the .vue filenames (delete the second one if you want one -# screen). Full checklist: app-profiles/template-bundle/README.md +# screen). Full checklist: plugin-designs/template-bundle/README.md # 3. it shows up in staging automatically — no list to touch bun run build # 4. when ready, publish ``` @@ -135,7 +135,7 @@ Arabic, Hindi, Chinese). The library is **Apache-2.0** — shared freely — but is entirely yours**. > **Change colors by editing values, not logic.** Your map's colors live in your own folder, -> `app-profiles//src/colors/`. Copy `example-mode.js`, rename it, change the hex +> `plugin-designs//src/colors/`. Copy `example-mode.js`, rename it, change the hex > values. Your colors merge *over* the shared set for your map only, so they can never break > another map. Never edit `library/colors/` to recolor your own map. diff --git a/index.html b/index.html index 951ad6f2a..90ab8e019 100644 --- a/index.html +++ b/index.html @@ -121,7 +121,7 @@
- Live proof of the bundler · bundles auto-detected from app-profiles/*/ · maps lazy-mounted, WebGL-context-safe. + Live proof of the bundler · bundles auto-detected from plugin-designs/*/ · maps lazy-mounted, WebGL-context-safe. “Write the vision, and make it plain … that he may run that readeth it.” — Habakkuk 2:2
diff --git a/internal/generate-gallery.mjs b/internal/generate-gallery.mjs index 2ea42b335..7c9a7bf65 100644 --- a/internal/generate-gallery.mjs +++ b/internal/generate-gallery.mjs @@ -7,7 +7,7 @@ * web-root, written by `bun run gallery` (last step of * `bun run build`) from public//manifest.json. * renderGallery(model, { mode: 'dev' }) what `bun run dev` serves at / — the same page over a - * manifest-shaped model of app-profiles/ (see + * manifest-shaped model of plugin-designs/ (see * internal/plugin-gallery/dev-plugin.mjs). * * public/ holds exactly three things: assets/ (data), / (the bundle folders + manifest.json) @@ -32,7 +32,7 @@ * (the manifest's embed.iframe / embed.script). * * What mode 'dev' changes — and nothing else: the loader imports the bundle's dev entry - * (scriptUrl = /app-profiles//index.js, an ES module Vite serves) instead of adding an + * (scriptUrl = /plugin-designs//index.js, an ES module Vite serves) instead of adding an * IIFE script tag; the live model is fetched from the dev route ( = __plugin-gallery); and * the header carries the composability ladder with its "?" popovers (kind explanations + the * "Start here" primer). The popovers stay out of the built page on purpose: they add ~8 KB of @@ -134,9 +134,9 @@ const LADDER_JS = String.raw` - + + window.FORGE.manifest = {"schema":"doxa-maps-manifest@1","generatedFrom":"plugin-designs/*/*.vue","embedBase":".","bundleCount":10,"profileCount":19,"bundles":[{"bundle":"all-people-groups","scriptUrl":"./all-people-groups/all-people-groups.js","output":"doxa-maps/all-people-groups/all-people-groups.js","integrity":"sha384-j5Ypx+EI9DGlTClzgLs1R8wAnmTnkQ7HgAhHrxyGoVoHDMTlFVY7slU6JNicn77T","elements":["all-people-groups"],"profiles":[{"profile":"all-people-groups","file":"all-people-groups.vue","description":"All 12,379 people groups (peoplegroups.org) in four colors — reached,","element":"all-people-groups","iframeUrl":"./all-people-groups/all-people-groups/index.html","scriptUrl":"./all-people-groups/all-people-groups.js","embed":{"iframe":"\u003ciframe src=\"./all-people-groups/all-people-groups/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./all-people-groups/all-people-groups.js\" integrity=\"sha384-j5Ypx+EI9DGlTClzgLs1R8wAnmTnkQ7HgAhHrxyGoVoHDMTlFVY7slU6JNicn77T\" crossorigin=\"anonymous\">\u003c/script>\n\u003call-people-groups profile-config='{\"profile\":\"all-people-groups\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/all-people-groups>"},"bundle":"all-people-groups"}]}]}; - + - + - + + window.FORGE.manifest = {"schema":"doxa-maps-manifest@1","generatedFrom":"plugin-designs/*/*.vue","embedBase":".","bundleCount":10,"profileCount":19,"bundles":[{"bundle":"cities-internet","scriptUrl":"./cities-internet/cities-internet.js","output":"doxa-maps/cities-internet/cities-internet.js","integrity":"sha384-gPdqkrwfePG3KfeoSRe6X6laMtcVdN7tqsJXm15WUA9UWWhxW9j99P+gyKe/dl8Z","elements":["cities-internet-map"],"profiles":[{"profile":"connectivity-list","file":"connectivity-list.vue","description":"","element":"cities-internet-map","iframeUrl":"./cities-internet/connectivity-list/index.html","scriptUrl":"./cities-internet/cities-internet.js","embed":{"iframe":"\u003ciframe src=\"./cities-internet/connectivity-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-gPdqkrwfePG3KfeoSRe6X6laMtcVdN7tqsJXm15WUA9UWWhxW9j99P+gyKe/dl8Z\" crossorigin=\"anonymous\">\u003c/script>\n\u003ccities-internet-map profile-config='{\"profile\":\"connectivity-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/cities-internet-map>"}},{"profile":"connectivity-map","file":"connectivity-map.vue","description":"","element":"cities-internet-map","iframeUrl":"./cities-internet/connectivity-map/index.html","scriptUrl":"./cities-internet/cities-internet.js","embed":{"iframe":"\u003ciframe src=\"./cities-internet/connectivity-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-gPdqkrwfePG3KfeoSRe6X6laMtcVdN7tqsJXm15WUA9UWWhxW9j99P+gyKe/dl8Z\" crossorigin=\"anonymous\">\u003c/script>\n\u003ccities-internet-map profile-config='{\"profile\":\"connectivity-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/cities-internet-map>"}},{"profile":"data-tables","file":"data-tables.vue","description":"","element":"cities-internet-map","iframeUrl":"./cities-internet/data-tables/index.html","scriptUrl":"./cities-internet/cities-internet.js","embed":{"iframe":"\u003ciframe src=\"./cities-internet/data-tables/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-gPdqkrwfePG3KfeoSRe6X6laMtcVdN7tqsJXm15WUA9UWWhxW9j99P+gyKe/dl8Z\" crossorigin=\"anonymous\">\u003c/script>\n\u003ccities-internet-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/cities-internet-map>"}}]}]}; - + - + + window.FORGE.manifest = {"schema":"doxa-maps-manifest@1","generatedFrom":"plugin-designs/*/*.vue","embedBase":".","bundleCount":10,"profileCount":19,"bundles":[{"bundle":"country-posters","scriptUrl":"./country-posters/country-posters.js","output":"doxa-maps/country-posters/country-posters.js","integrity":"sha384-X9SslLiOGDh8ASpogPFqNUI34WlQHj7pMHc2hPWMQxnuZfUk67ZFpnBa17eRFLcD","elements":["country-posters-map"],"profiles":[{"profile":"countries-map","file":"countries-map.vue","description":"","element":"country-posters-map","iframeUrl":"./country-posters/countries-map/index.html","scriptUrl":"./country-posters/country-posters.js","embed":{"iframe":"\u003ciframe src=\"./country-posters/countries-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./country-posters/country-posters.js\" integrity=\"sha384-X9SslLiOGDh8ASpogPFqNUI34WlQHj7pMHc2hPWMQxnuZfUk67ZFpnBa17eRFLcD\" crossorigin=\"anonymous\">\u003c/script>\n\u003ccountry-posters-map profile-config='{\"profile\":\"countries-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/country-posters-map>"}},{"profile":"country-poster-wall","file":"country-poster-wall.vue","description":"","element":"country-posters-map","iframeUrl":"./country-posters/country-poster-wall/index.html","scriptUrl":"./country-posters/country-posters.js","embed":{"iframe":"\u003ciframe src=\"./country-posters/country-poster-wall/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./country-posters/country-posters.js\" integrity=\"sha384-X9SslLiOGDh8ASpogPFqNUI34WlQHj7pMHc2hPWMQxnuZfUk67ZFpnBa17eRFLcD\" crossorigin=\"anonymous\">\u003c/script>\n\u003ccountry-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/country-posters-map>"}}]}]}; - + - + + window.FORGE.manifest = {"schema":"doxa-maps-manifest@1","generatedFrom":"plugin-designs/*/*.vue","embedBase":".","bundleCount":10,"profileCount":19,"bundles":[{"bundle":"demo-map","scriptUrl":"./demo-map/demo-map.js","output":"doxa-maps/demo-map/demo-map.js","integrity":"sha384-3Yb2aEbNtFMFCI+I0oo1qCkx1HeP+oOpeOCLEPXpnZ3tNMqDSpxfWuv++nrmXgRc","elements":["demo-map"],"profiles":[{"profile":"demo-map","file":"demo-map.vue","description":"Simple two-tab demo map. Tab 1 colors pins by religion (shared","element":"demo-map","iframeUrl":"./demo-map/demo-map/index.html","scriptUrl":"./demo-map/demo-map.js","embed":{"iframe":"\u003ciframe src=\"./demo-map/demo-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./demo-map/demo-map.js\" integrity=\"sha384-3Yb2aEbNtFMFCI+I0oo1qCkx1HeP+oOpeOCLEPXpnZ3tNMqDSpxfWuv++nrmXgRc\" crossorigin=\"anonymous\">\u003c/script>\n\u003cdemo-map profile-config='{\"profile\":\"demo-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/demo-map>"},"instances":[{"id":"maplibre","title":"Demo map — MapLibre (token-free)","apiBaseUrl":"","props":{"profile":"demo-map","provider":"maplibre","instanceId":"demo-maplibre"},"element":"demo-map","iframeUrl":"./demo-map/demo-map/maplibre/index.html","embed":{"iframe":"\u003ciframe src=\"./demo-map/demo-map/maplibre/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}}],"bundle":"demo-map"}]}]}; + window.FORGE.manifest = {"schema":"doxa-maps-manifest@1","generatedFrom":"plugin-designs/*/*.vue","embedBase":".","bundleCount":10,"profileCount":19,"bundles":[{"bundle":"doxa-research-map","scriptUrl":"./doxa-research-map/doxa-research-map.js","output":"doxa-maps/doxa-research-map/doxa-research-map.js","integrity":"sha384-7u3ES3Q7Cm0CekKhxsv452xTCCJZcUc03nMDq+7pyeYxtJQ4Jx15qzfBxvaGkS2D","elements":["doxa-research-map"],"profiles":[{"profile":"research-map","file":"research-map.vue","description":"","element":"doxa-research-map","iframeUrl":"./doxa-research-map/research-map/index.html","scriptUrl":"./doxa-research-map/doxa-research-map.js","embed":{"iframe":"\u003ciframe src=\"./doxa-research-map/research-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./doxa-research-map/doxa-research-map.js\" integrity=\"sha384-7u3ES3Q7Cm0CekKhxsv452xTCCJZcUc03nMDq+7pyeYxtJQ4Jx15qzfBxvaGkS2D\" crossorigin=\"anonymous\">\u003c/script>\n\u003cdoxa-research-map profile-config='{\"profile\":\"research-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/doxa-research-map>"}}]}]}; - + - + - + - + - + + window.FORGE.manifest = {"schema":"doxa-maps-manifest@1","generatedFrom":"plugin-designs/*/*.vue","embedBase":".","bundleCount":10,"profileCount":19,"bundles":[{"bundle":"doxa-simple-map","scriptUrl":"./doxa-simple-map/doxa-simple-map.js","output":"doxa-maps/doxa-simple-map/doxa-simple-map.js","integrity":"sha384-D0yE08hqLleScKwoQQEjOk33/rEzprHxU1ECExCZFrANBQIBdh3OI3TNRLPZpE5f","elements":["doxa-map","doxa-simple-map"],"profiles":[{"profile":"doxa-simple-map","file":"doxa-simple-map.vue","description":"","element":"doxa-map","iframeUrl":"./doxa-simple-map/doxa-simple-map/index.html","scriptUrl":"./doxa-simple-map/doxa-simple-map.js","embed":{"iframe":"\u003ciframe src=\"./doxa-simple-map/doxa-simple-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-D0yE08hqLleScKwoQQEjOk33/rEzprHxU1ECExCZFrANBQIBdh3OI3TNRLPZpE5f\" crossorigin=\"anonymous\">\u003c/script>\n\u003cdoxa-map profile-config='{\"profile\":\"doxa-simple-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/doxa-map>"},"instances":[{"id":"prayer","title":"Prayer Map","apiBaseUrl":"https://pray.doxa.life","props":{"profile":"doxa-simple-map","instanceId":"embed-prayer","dataSource":"pray-tools","tabs":[{"id":"prayer","colorStrategy":"prayer","legend":"prayer","popup":"prayer"}]},"element":"doxa-map","iframeUrl":"./doxa-simple-map/doxa-simple-map/prayer/index.html","embed":{"iframe":"\u003ciframe src=\"./doxa-simple-map/doxa-simple-map/prayer/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}},{"id":"engagement","title":"Engagement Map","apiBaseUrl":"https://pray.doxa.life","props":{"profile":"doxa-simple-map","instanceId":"embed-engagement","dataSource":"pray-tools","tabs":[{"id":"engagement","colorStrategy":"engagement","legend":"engagement","popup":"engagement"}]},"element":"doxa-map","iframeUrl":"./doxa-simple-map/doxa-simple-map/engagement/index.html","embed":{"iframe":"\u003ciframe src=\"./doxa-simple-map/doxa-simple-map/engagement/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}},{"id":"adoption","title":"Adoption Map","apiBaseUrl":"https://pray.doxa.life","props":{"profile":"doxa-simple-map","instanceId":"embed-adoption","dataSource":"pray-tools","tabs":[{"id":"adoption","colorStrategy":"adoption","legend":"adoption","popup":"adoption"}]},"element":"doxa-map","iframeUrl":"./doxa-simple-map/doxa-simple-map/adoption/index.html","embed":{"iframe":"\u003ciframe src=\"./doxa-simple-map/doxa-simple-map/adoption/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}}]}]}]}; - + - + + window.FORGE.manifest = {"schema":"doxa-maps-manifest@1","generatedFrom":"plugin-designs/*/*.vue","embedBase":".","bundleCount":10,"profileCount":19,"bundles":[{"bundle":"gospel-resources","scriptUrl":"./gospel-resources/gospel-resources.js","output":"doxa-maps/gospel-resources/gospel-resources.js","integrity":"sha384-PsNBJxT6xrftrL2YX32rTrg63HTAa3axny0Udu+4LSvRQt8mTlTlZ51FTxTj2+S2","elements":["gospel-resources-map"],"profiles":[{"profile":"charts","file":"charts.vue","description":"","element":"gospel-resources-map","iframeUrl":"./gospel-resources/charts/index.html","scriptUrl":"./gospel-resources/gospel-resources.js","embed":{"iframe":"\u003ciframe src=\"./gospel-resources/charts/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-PsNBJxT6xrftrL2YX32rTrg63HTAa3axny0Udu+4LSvRQt8mTlTlZ51FTxTj2+S2\" crossorigin=\"anonymous\">\u003c/script>\n\u003cgospel-resources-map profile-config='{\"profile\":\"charts\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/gospel-resources-map>"}},{"profile":"data-tables","file":"data-tables.vue","description":"","element":"gospel-resources-map","iframeUrl":"./gospel-resources/data-tables/index.html","scriptUrl":"./gospel-resources/gospel-resources.js","embed":{"iframe":"\u003ciframe src=\"./gospel-resources/data-tables/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-PsNBJxT6xrftrL2YX32rTrg63HTAa3axny0Udu+4LSvRQt8mTlTlZ51FTxTj2+S2\" crossorigin=\"anonymous\">\u003c/script>\n\u003cgospel-resources-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/gospel-resources-map>"}},{"profile":"resources-list","file":"resources-list.vue","description":"","element":"gospel-resources-map","iframeUrl":"./gospel-resources/resources-list/index.html","scriptUrl":"./gospel-resources/gospel-resources.js","embed":{"iframe":"\u003ciframe src=\"./gospel-resources/resources-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-PsNBJxT6xrftrL2YX32rTrg63HTAa3axny0Udu+4LSvRQt8mTlTlZ51FTxTj2+S2\" crossorigin=\"anonymous\">\u003c/script>\n\u003cgospel-resources-map profile-config='{\"profile\":\"resources-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/gospel-resources-map>"}},{"profile":"resources-map","file":"resources-map.vue","description":"","element":"gospel-resources-map","iframeUrl":"./gospel-resources/resources-map/index.html","scriptUrl":"./gospel-resources/gospel-resources.js","embed":{"iframe":"\u003ciframe src=\"./gospel-resources/resources-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-PsNBJxT6xrftrL2YX32rTrg63HTAa3axny0Udu+4LSvRQt8mTlTlZ51FTxTj2+S2\" crossorigin=\"anonymous\">\u003c/script>\n\u003cgospel-resources-map profile-config='{\"profile\":\"resources-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/gospel-resources-map>"}}]}]}; - + - + + window.FORGE.manifest = {"schema":"doxa-maps-manifest@1","generatedFrom":"plugin-designs/*/*.vue","embedBase":".","bundleCount":10,"profileCount":19,"bundles":[{"bundle":"all-people-groups","scriptUrl":"./all-people-groups/all-people-groups.js","output":"doxa-maps/all-people-groups/all-people-groups.js","integrity":"sha384-j5Ypx+EI9DGlTClzgLs1R8wAnmTnkQ7HgAhHrxyGoVoHDMTlFVY7slU6JNicn77T","elements":["all-people-groups"],"profiles":[{"profile":"all-people-groups","file":"all-people-groups.vue","description":"All 12,379 people groups (peoplegroups.org) in four colors — reached,","element":"all-people-groups","iframeUrl":"./all-people-groups/all-people-groups/index.html","scriptUrl":"./all-people-groups/all-people-groups.js","embed":{"iframe":"\u003ciframe src=\"./all-people-groups/all-people-groups/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./all-people-groups/all-people-groups.js\" integrity=\"sha384-j5Ypx+EI9DGlTClzgLs1R8wAnmTnkQ7HgAhHrxyGoVoHDMTlFVY7slU6JNicn77T\" crossorigin=\"anonymous\">\u003c/script>\n\u003call-people-groups profile-config='{\"profile\":\"all-people-groups\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/all-people-groups>"},"bundle":"all-people-groups"}]},{"bundle":"cities-internet","scriptUrl":"./cities-internet/cities-internet.js","output":"doxa-maps/cities-internet/cities-internet.js","integrity":"sha384-gPdqkrwfePG3KfeoSRe6X6laMtcVdN7tqsJXm15WUA9UWWhxW9j99P+gyKe/dl8Z","elements":["cities-internet-map"],"profiles":[{"profile":"connectivity-list","file":"connectivity-list.vue","description":"","element":"cities-internet-map","iframeUrl":"./cities-internet/connectivity-list/index.html","scriptUrl":"./cities-internet/cities-internet.js","embed":{"iframe":"\u003ciframe src=\"./cities-internet/connectivity-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-gPdqkrwfePG3KfeoSRe6X6laMtcVdN7tqsJXm15WUA9UWWhxW9j99P+gyKe/dl8Z\" crossorigin=\"anonymous\">\u003c/script>\n\u003ccities-internet-map profile-config='{\"profile\":\"connectivity-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/cities-internet-map>"}},{"profile":"connectivity-map","file":"connectivity-map.vue","description":"","element":"cities-internet-map","iframeUrl":"./cities-internet/connectivity-map/index.html","scriptUrl":"./cities-internet/cities-internet.js","embed":{"iframe":"\u003ciframe src=\"./cities-internet/connectivity-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-gPdqkrwfePG3KfeoSRe6X6laMtcVdN7tqsJXm15WUA9UWWhxW9j99P+gyKe/dl8Z\" crossorigin=\"anonymous\">\u003c/script>\n\u003ccities-internet-map profile-config='{\"profile\":\"connectivity-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/cities-internet-map>"}},{"profile":"data-tables","file":"data-tables.vue","description":"","element":"cities-internet-map","iframeUrl":"./cities-internet/data-tables/index.html","scriptUrl":"./cities-internet/cities-internet.js","embed":{"iframe":"\u003ciframe src=\"./cities-internet/data-tables/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-gPdqkrwfePG3KfeoSRe6X6laMtcVdN7tqsJXm15WUA9UWWhxW9j99P+gyKe/dl8Z\" crossorigin=\"anonymous\">\u003c/script>\n\u003ccities-internet-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/cities-internet-map>"}}]},{"bundle":"country-posters","scriptUrl":"./country-posters/country-posters.js","output":"doxa-maps/country-posters/country-posters.js","integrity":"sha384-X9SslLiOGDh8ASpogPFqNUI34WlQHj7pMHc2hPWMQxnuZfUk67ZFpnBa17eRFLcD","elements":["country-posters-map"],"profiles":[{"profile":"countries-map","file":"countries-map.vue","description":"","element":"country-posters-map","iframeUrl":"./country-posters/countries-map/index.html","scriptUrl":"./country-posters/country-posters.js","embed":{"iframe":"\u003ciframe src=\"./country-posters/countries-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./country-posters/country-posters.js\" integrity=\"sha384-X9SslLiOGDh8ASpogPFqNUI34WlQHj7pMHc2hPWMQxnuZfUk67ZFpnBa17eRFLcD\" crossorigin=\"anonymous\">\u003c/script>\n\u003ccountry-posters-map profile-config='{\"profile\":\"countries-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/country-posters-map>"}},{"profile":"country-poster-wall","file":"country-poster-wall.vue","description":"","element":"country-posters-map","iframeUrl":"./country-posters/country-poster-wall/index.html","scriptUrl":"./country-posters/country-posters.js","embed":{"iframe":"\u003ciframe src=\"./country-posters/country-poster-wall/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./country-posters/country-posters.js\" integrity=\"sha384-X9SslLiOGDh8ASpogPFqNUI34WlQHj7pMHc2hPWMQxnuZfUk67ZFpnBa17eRFLcD\" crossorigin=\"anonymous\">\u003c/script>\n\u003ccountry-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/country-posters-map>"}}]},{"bundle":"demo-map","scriptUrl":"./demo-map/demo-map.js","output":"doxa-maps/demo-map/demo-map.js","integrity":"sha384-3Yb2aEbNtFMFCI+I0oo1qCkx1HeP+oOpeOCLEPXpnZ3tNMqDSpxfWuv++nrmXgRc","elements":["demo-map"],"profiles":[{"profile":"demo-map","file":"demo-map.vue","description":"Simple two-tab demo map. Tab 1 colors pins by religion (shared","element":"demo-map","iframeUrl":"./demo-map/demo-map/index.html","scriptUrl":"./demo-map/demo-map.js","embed":{"iframe":"\u003ciframe src=\"./demo-map/demo-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./demo-map/demo-map.js\" integrity=\"sha384-3Yb2aEbNtFMFCI+I0oo1qCkx1HeP+oOpeOCLEPXpnZ3tNMqDSpxfWuv++nrmXgRc\" crossorigin=\"anonymous\">\u003c/script>\n\u003cdemo-map profile-config='{\"profile\":\"demo-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/demo-map>"},"instances":[{"id":"maplibre","title":"Demo map — MapLibre (token-free)","apiBaseUrl":"","props":{"profile":"demo-map","provider":"maplibre","instanceId":"demo-maplibre"},"element":"demo-map","iframeUrl":"./demo-map/demo-map/maplibre/index.html","embed":{"iframe":"\u003ciframe src=\"./demo-map/demo-map/maplibre/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}}],"bundle":"demo-map"}]},{"bundle":"doxa-research-map","scriptUrl":"./doxa-research-map/doxa-research-map.js","output":"doxa-maps/doxa-research-map/doxa-research-map.js","integrity":"sha384-7u3ES3Q7Cm0CekKhxsv452xTCCJZcUc03nMDq+7pyeYxtJQ4Jx15qzfBxvaGkS2D","elements":["doxa-research-map"],"profiles":[{"profile":"research-map","file":"research-map.vue","description":"","element":"doxa-research-map","iframeUrl":"./doxa-research-map/research-map/index.html","scriptUrl":"./doxa-research-map/doxa-research-map.js","embed":{"iframe":"\u003ciframe src=\"./doxa-research-map/research-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./doxa-research-map/doxa-research-map.js\" integrity=\"sha384-7u3ES3Q7Cm0CekKhxsv452xTCCJZcUc03nMDq+7pyeYxtJQ4Jx15qzfBxvaGkS2D\" crossorigin=\"anonymous\">\u003c/script>\n\u003cdoxa-research-map profile-config='{\"profile\":\"research-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/doxa-research-map>"}}]},{"bundle":"doxa-simple-map","scriptUrl":"./doxa-simple-map/doxa-simple-map.js","output":"doxa-maps/doxa-simple-map/doxa-simple-map.js","integrity":"sha384-D0yE08hqLleScKwoQQEjOk33/rEzprHxU1ECExCZFrANBQIBdh3OI3TNRLPZpE5f","elements":["doxa-map","doxa-simple-map"],"profiles":[{"profile":"doxa-simple-map","file":"doxa-simple-map.vue","description":"","element":"doxa-map","iframeUrl":"./doxa-simple-map/doxa-simple-map/index.html","scriptUrl":"./doxa-simple-map/doxa-simple-map.js","embed":{"iframe":"\u003ciframe src=\"./doxa-simple-map/doxa-simple-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-D0yE08hqLleScKwoQQEjOk33/rEzprHxU1ECExCZFrANBQIBdh3OI3TNRLPZpE5f\" crossorigin=\"anonymous\">\u003c/script>\n\u003cdoxa-map profile-config='{\"profile\":\"doxa-simple-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/doxa-map>"},"instances":[{"id":"prayer","title":"Prayer Map","apiBaseUrl":"https://pray.doxa.life","props":{"profile":"doxa-simple-map","instanceId":"embed-prayer","dataSource":"pray-tools","tabs":[{"id":"prayer","colorStrategy":"prayer","legend":"prayer","popup":"prayer"}]},"element":"doxa-map","iframeUrl":"./doxa-simple-map/doxa-simple-map/prayer/index.html","embed":{"iframe":"\u003ciframe src=\"./doxa-simple-map/doxa-simple-map/prayer/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}},{"id":"engagement","title":"Engagement Map","apiBaseUrl":"https://pray.doxa.life","props":{"profile":"doxa-simple-map","instanceId":"embed-engagement","dataSource":"pray-tools","tabs":[{"id":"engagement","colorStrategy":"engagement","legend":"engagement","popup":"engagement"}]},"element":"doxa-map","iframeUrl":"./doxa-simple-map/doxa-simple-map/engagement/index.html","embed":{"iframe":"\u003ciframe src=\"./doxa-simple-map/doxa-simple-map/engagement/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}},{"id":"adoption","title":"Adoption Map","apiBaseUrl":"https://pray.doxa.life","props":{"profile":"doxa-simple-map","instanceId":"embed-adoption","dataSource":"pray-tools","tabs":[{"id":"adoption","colorStrategy":"adoption","legend":"adoption","popup":"adoption"}]},"element":"doxa-map","iframeUrl":"./doxa-simple-map/doxa-simple-map/adoption/index.html","embed":{"iframe":"\u003ciframe src=\"./doxa-simple-map/doxa-simple-map/adoption/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}}]}]},{"bundle":"gospel-resources","scriptUrl":"./gospel-resources/gospel-resources.js","output":"doxa-maps/gospel-resources/gospel-resources.js","integrity":"sha384-PsNBJxT6xrftrL2YX32rTrg63HTAa3axny0Udu+4LSvRQt8mTlTlZ51FTxTj2+S2","elements":["gospel-resources-map"],"profiles":[{"profile":"charts","file":"charts.vue","description":"","element":"gospel-resources-map","iframeUrl":"./gospel-resources/charts/index.html","scriptUrl":"./gospel-resources/gospel-resources.js","embed":{"iframe":"\u003ciframe src=\"./gospel-resources/charts/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-PsNBJxT6xrftrL2YX32rTrg63HTAa3axny0Udu+4LSvRQt8mTlTlZ51FTxTj2+S2\" crossorigin=\"anonymous\">\u003c/script>\n\u003cgospel-resources-map profile-config='{\"profile\":\"charts\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/gospel-resources-map>"}},{"profile":"data-tables","file":"data-tables.vue","description":"","element":"gospel-resources-map","iframeUrl":"./gospel-resources/data-tables/index.html","scriptUrl":"./gospel-resources/gospel-resources.js","embed":{"iframe":"\u003ciframe src=\"./gospel-resources/data-tables/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-PsNBJxT6xrftrL2YX32rTrg63HTAa3axny0Udu+4LSvRQt8mTlTlZ51FTxTj2+S2\" crossorigin=\"anonymous\">\u003c/script>\n\u003cgospel-resources-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/gospel-resources-map>"}},{"profile":"resources-list","file":"resources-list.vue","description":"","element":"gospel-resources-map","iframeUrl":"./gospel-resources/resources-list/index.html","scriptUrl":"./gospel-resources/gospel-resources.js","embed":{"iframe":"\u003ciframe src=\"./gospel-resources/resources-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-PsNBJxT6xrftrL2YX32rTrg63HTAa3axny0Udu+4LSvRQt8mTlTlZ51FTxTj2+S2\" crossorigin=\"anonymous\">\u003c/script>\n\u003cgospel-resources-map profile-config='{\"profile\":\"resources-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/gospel-resources-map>"}},{"profile":"resources-map","file":"resources-map.vue","description":"","element":"gospel-resources-map","iframeUrl":"./gospel-resources/resources-map/index.html","scriptUrl":"./gospel-resources/gospel-resources.js","embed":{"iframe":"\u003ciframe src=\"./gospel-resources/resources-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-PsNBJxT6xrftrL2YX32rTrg63HTAa3axny0Udu+4LSvRQt8mTlTlZ51FTxTj2+S2\" crossorigin=\"anonymous\">\u003c/script>\n\u003cgospel-resources-map profile-config='{\"profile\":\"resources-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/gospel-resources-map>"}}]},{"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-/1oZAkM+5gTg7nSy5yO4Dbd2CA8OXmIW7dqCVDiYgAnWy5++JNeu6JtdzTEYtqmV","elements":["my-upg-100-list-map"],"profiles":[{"profile":"poster-wall","file":"poster-wall.vue","description":"","element":"my-upg-100-list-map","iframeUrl":"./my-upg-100-list/poster-wall/index.html","scriptUrl":"./my-upg-100-list/my-upg-100-list.js","embed":{"iframe":"\u003ciframe src=\"./my-upg-100-list/poster-wall/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-/1oZAkM+5gTg7nSy5yO4Dbd2CA8OXmIW7dqCVDiYgAnWy5++JNeu6JtdzTEYtqmV\" crossorigin=\"anonymous\">\u003c/script>\n\u003cmy-upg-100-list-map profile-config='{\"profile\":\"poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/my-upg-100-list-map>"}},{"profile":"upg-100-list","file":"upg-100-list.vue","description":"","element":"my-upg-100-list-map","iframeUrl":"./my-upg-100-list/upg-100-list/index.html","scriptUrl":"./my-upg-100-list/my-upg-100-list.js","embed":{"iframe":"\u003ciframe 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>\u003c/iframe>","script":"\u003cscript src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-/1oZAkM+5gTg7nSy5yO4Dbd2CA8OXmIW7dqCVDiYgAnWy5++JNeu6JtdzTEYtqmV\" crossorigin=\"anonymous\">\u003c/script>\n\u003cmy-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/my-upg-100-list-map>"}},{"profile":"upg-100-mfe","file":"upg-100-mfe.vue","description":"","element":"my-upg-100-list-map","iframeUrl":"./my-upg-100-list/upg-100-mfe/index.html","scriptUrl":"./my-upg-100-list/my-upg-100-list.js","embed":{"iframe":"\u003ciframe 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>\u003c/iframe>","script":"\u003cscript src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-/1oZAkM+5gTg7nSy5yO4Dbd2CA8OXmIW7dqCVDiYgAnWy5++JNeu6JtdzTEYtqmV\" crossorigin=\"anonymous\">\u003c/script>\n\u003cmy-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/my-upg-100-list-map>"}}]},{"bundle":"research-dashboard","scriptUrl":"./research-dashboard/research-dashboard.js","output":"doxa-maps/research-dashboard/research-dashboard.js","integrity":"sha384-iiwu9NS8aQ4JKQ2ksPF487DAN3ktlPs/3fTdkoqe/opPMCY91PqnT354dLhMBlM2","elements":["research-dashboard-map"],"profiles":[{"profile":"research-dashboard","file":"research-dashboard.vue","description":"Coordinated-Multiple-Views research dashboard for 10/40-window UPGs (live pray.doxa.life): docked facet rail + map-as-hero + linked table/charts, brushing-and-linking over one in-memory store.","element":"research-dashboard-map","iframeUrl":"./research-dashboard/research-dashboard/index.html","scriptUrl":"./research-dashboard/research-dashboard.js","embed":{"iframe":"\u003ciframe src=\"./research-dashboard/research-dashboard/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./research-dashboard/research-dashboard.js\" integrity=\"sha384-iiwu9NS8aQ4JKQ2ksPF487DAN3ktlPs/3fTdkoqe/opPMCY91PqnT354dLhMBlM2\" crossorigin=\"anonymous\">\u003c/script>\n\u003cresearch-dashboard-map profile-config='{\"profile\":\"research-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/research-dashboard-map>"},"bundle":"research-dashboard"}]},{"bundle":"template-bundle","scriptUrl":"./template-bundle/template-bundle.js","output":"doxa-maps/template-bundle/template-bundle.js","integrity":"sha384-FwwULnUkiVHcc88AwL8QA5fGsZRqLnQHPqXmug5hxtCyTvC8VIdmcnXDJ+KvhHpm","elements":["template-bundle"],"profiles":[{"profile":"template-map-a","file":"template-map-a.vue","description":"Minimal example profile A — a colored placeholder, no Mapbox or data.","element":"template-bundle","iframeUrl":"./template-bundle/template-map-a/index.html","scriptUrl":"./template-bundle/template-bundle.js","embed":{"iframe":"\u003ciframe src=\"./template-bundle/template-map-a/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./template-bundle/template-bundle.js\" integrity=\"sha384-FwwULnUkiVHcc88AwL8QA5fGsZRqLnQHPqXmug5hxtCyTvC8VIdmcnXDJ+KvhHpm\" crossorigin=\"anonymous\">\u003c/script>\n\u003ctemplate-bundle profile-config='{\"profile\":\"template-map-a\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/template-bundle>"},"bundle":"template-bundle"},{"profile":"template-map-b","file":"template-map-b.vue","description":"Minimal example profile B — a second profile in the same bundle.","element":"template-bundle","iframeUrl":"./template-bundle/template-map-b/index.html","scriptUrl":"./template-bundle/template-bundle.js","embed":{"iframe":"\u003ciframe src=\"./template-bundle/template-map-b/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./template-bundle/template-bundle.js\" integrity=\"sha384-FwwULnUkiVHcc88AwL8QA5fGsZRqLnQHPqXmug5hxtCyTvC8VIdmcnXDJ+KvhHpm\" crossorigin=\"anonymous\">\u003c/script>\n\u003ctemplate-bundle profile-config='{\"profile\":\"template-map-b\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/template-bundle>"},"bundle":"template-bundle"}]}]}; \n" + "script": "\n" }, "bundle": "all-people-groups" } @@ -33,7 +33,7 @@ "bundle": "cities-internet", "scriptUrl": "./cities-internet/cities-internet.js", "output": "doxa-maps/cities-internet/cities-internet.js", - "integrity": "sha384-PAqYfQxuqlTa0wMwATfLtaJIeDnBhlHqijIMW4ILMtpy4VOXubmV19L8iiu3LI/u", + "integrity": "sha384-gPdqkrwfePG3KfeoSRe6X6laMtcVdN7tqsJXm15WUA9UWWhxW9j99P+gyKe/dl8Z", "elements": [ "cities-internet-map" ], @@ -47,7 +47,7 @@ "scriptUrl": "./cities-internet/cities-internet.js", "embed": { "iframe": "", - "script": "\n" + "script": "\n" } }, { @@ -59,7 +59,7 @@ "scriptUrl": "./cities-internet/cities-internet.js", "embed": { "iframe": "", - "script": "\n" + "script": "\n" } }, { @@ -71,7 +71,7 @@ "scriptUrl": "./cities-internet/cities-internet.js", "embed": { "iframe": "", - "script": "\n" + "script": "\n" } } ] @@ -80,7 +80,7 @@ "bundle": "country-posters", "scriptUrl": "./country-posters/country-posters.js", "output": "doxa-maps/country-posters/country-posters.js", - "integrity": "sha384-55+Ke4uM9UOSblliojP0Q/PjsXxJ4eeO2Pq8zSIC6m4wPq18T2QCSTyM1NMBz0bo", + "integrity": "sha384-X9SslLiOGDh8ASpogPFqNUI34WlQHj7pMHc2hPWMQxnuZfUk67ZFpnBa17eRFLcD", "elements": [ "country-posters-map" ], @@ -94,7 +94,7 @@ "scriptUrl": "./country-posters/country-posters.js", "embed": { "iframe": "", - "script": "\n" + "script": "\n" } }, { @@ -106,7 +106,7 @@ "scriptUrl": "./country-posters/country-posters.js", "embed": { "iframe": "", - "script": "\n" + "script": "\n" } } ] @@ -115,7 +115,7 @@ "bundle": "demo-map", "scriptUrl": "./demo-map/demo-map.js", "output": "doxa-maps/demo-map/demo-map.js", - "integrity": "sha384-GA5KpxntQ8Dm16XW3EFTfwVVAP3hR3/GuDN3H8rrUKrS7zWJMikEYOoA5Pqk8COy", + "integrity": "sha384-3Yb2aEbNtFMFCI+I0oo1qCkx1HeP+oOpeOCLEPXpnZ3tNMqDSpxfWuv++nrmXgRc", "elements": [ "demo-map" ], @@ -129,7 +129,7 @@ "scriptUrl": "./demo-map/demo-map.js", "embed": { "iframe": "", - "script": "\n" + "script": "\n" }, "instances": [ { @@ -156,7 +156,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-/v+43kKeEvsMUX//oDpIDN8+3l/NGhUJRGBg0jX9mNpIX/kTHZ/2Mss0oYGWlzeI", + "integrity": "sha384-7u3ES3Q7Cm0CekKhxsv452xTCCJZcUc03nMDq+7pyeYxtJQ4Jx15qzfBxvaGkS2D", "elements": [ "doxa-research-map" ], @@ -170,7 +170,7 @@ "scriptUrl": "./doxa-research-map/doxa-research-map.js", "embed": { "iframe": "", - "script": "\n" + "script": "\n" } } ] @@ -179,7 +179,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-PlKKpCWRvGpXosDd+yWeHGcGSUZg12corGjiRY9YiYby5k7YjT6FH0HGvWQiB+hi", + "integrity": "sha384-D0yE08hqLleScKwoQQEjOk33/rEzprHxU1ECExCZFrANBQIBdh3OI3TNRLPZpE5f", "elements": [ "doxa-map", "doxa-simple-map" @@ -194,7 +194,7 @@ "scriptUrl": "./doxa-simple-map/doxa-simple-map.js", "embed": { "iframe": "", - "script": "\n" + "script": "\n" }, "instances": [ { @@ -274,7 +274,7 @@ "bundle": "gospel-resources", "scriptUrl": "./gospel-resources/gospel-resources.js", "output": "doxa-maps/gospel-resources/gospel-resources.js", - "integrity": "sha384-mV+vHb0OoS7EeSBZosPxpN5q4wxOZWglWqScjjxqrGYeWdKr7aJjhGeqatnb2NTX", + "integrity": "sha384-PsNBJxT6xrftrL2YX32rTrg63HTAa3axny0Udu+4LSvRQt8mTlTlZ51FTxTj2+S2", "elements": [ "gospel-resources-map" ], @@ -288,7 +288,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n" + "script": "\n" } }, { @@ -300,7 +300,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n" + "script": "\n" } }, { @@ -312,7 +312,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n" + "script": "\n" } }, { @@ -324,7 +324,7 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n" + "script": "\n" } } ] @@ -333,7 +333,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-dQSNqodw7Tc4Jj7fKEWMFG2Qd0jQOAAaGTkStPU1YThiRuTlSAKWvRKjWZHJGfdY", + "integrity": "sha384-/1oZAkM+5gTg7nSy5yO4Dbd2CA8OXmIW7dqCVDiYgAnWy5++JNeu6JtdzTEYtqmV", "elements": [ "my-upg-100-list-map" ], @@ -347,7 +347,7 @@ "scriptUrl": "./my-upg-100-list/my-upg-100-list.js", "embed": { "iframe": "", - "script": "\n" + "script": "\n" } }, { @@ -359,7 +359,7 @@ "scriptUrl": "./my-upg-100-list/my-upg-100-list.js", "embed": { "iframe": "", - "script": "\n" + "script": "\n" } }, { @@ -371,7 +371,7 @@ "scriptUrl": "./my-upg-100-list/my-upg-100-list.js", "embed": { "iframe": "", - "script": "\n" + "script": "\n" } } ] @@ -380,7 +380,7 @@ "bundle": "research-dashboard", "scriptUrl": "./research-dashboard/research-dashboard.js", "output": "doxa-maps/research-dashboard/research-dashboard.js", - "integrity": "sha384-vc0eyZY70KkXAfbGXR7WTuG+vnrf6F+Q5n2EN90STiFqDBL2f0U5k2PfX9NyNlXv", + "integrity": "sha384-iiwu9NS8aQ4JKQ2ksPF487DAN3ktlPs/3fTdkoqe/opPMCY91PqnT354dLhMBlM2", "elements": [ "research-dashboard-map" ], @@ -394,7 +394,7 @@ "scriptUrl": "./research-dashboard/research-dashboard.js", "embed": { "iframe": "", - "script": "\n" + "script": "\n" }, "bundle": "research-dashboard" } @@ -404,7 +404,7 @@ "bundle": "template-bundle", "scriptUrl": "./template-bundle/template-bundle.js", "output": "doxa-maps/template-bundle/template-bundle.js", - "integrity": "sha384-QWFKdroBy8m2AbNlObiZP8cWs+mSHxRUGsFsdPCdmwk4NndJ/AY9pBLgwaVRNcA1", + "integrity": "sha384-FwwULnUkiVHcc88AwL8QA5fGsZRqLnQHPqXmug5hxtCyTvC8VIdmcnXDJ+KvhHpm", "elements": [ "template-bundle" ], @@ -418,7 +418,7 @@ "scriptUrl": "./template-bundle/template-bundle.js", "embed": { "iframe": "", - "script": "\n" + "script": "\n" }, "bundle": "template-bundle" }, @@ -431,7 +431,7 @@ "scriptUrl": "./template-bundle/template-bundle.js", "embed": { "iframe": "", - "script": "\n" + "script": "\n" }, "bundle": "template-bundle" } diff --git a/public/doxa-maps/my-upg-100-list/index.html b/public/doxa-maps/my-upg-100-list/index.html index 0824b302f..168fbf982 100644 --- a/public/doxa-maps/my-upg-100-list/index.html +++ b/public/doxa-maps/my-upg-100-list/index.html @@ -130,7 +130,7 @@ header.top .sub { margin-top: 3px; font: 12px/1.4 var(--sans); color: var(--mut) + window.FORGE.manifest = {"schema":"doxa-maps-manifest@1","generatedFrom":"plugin-designs/*/*.vue","embedBase":".","bundleCount":10,"profileCount":19,"bundles":[{"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-/1oZAkM+5gTg7nSy5yO4Dbd2CA8OXmIW7dqCVDiYgAnWy5++JNeu6JtdzTEYtqmV","elements":["my-upg-100-list-map"],"profiles":[{"profile":"poster-wall","file":"poster-wall.vue","description":"","element":"my-upg-100-list-map","iframeUrl":"./my-upg-100-list/poster-wall/index.html","scriptUrl":"./my-upg-100-list/my-upg-100-list.js","embed":{"iframe":"\u003ciframe src=\"./my-upg-100-list/poster-wall/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-/1oZAkM+5gTg7nSy5yO4Dbd2CA8OXmIW7dqCVDiYgAnWy5++JNeu6JtdzTEYtqmV\" crossorigin=\"anonymous\">\u003c/script>\n\u003cmy-upg-100-list-map profile-config='{\"profile\":\"poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/my-upg-100-list-map>"}},{"profile":"upg-100-list","file":"upg-100-list.vue","description":"","element":"my-upg-100-list-map","iframeUrl":"./my-upg-100-list/upg-100-list/index.html","scriptUrl":"./my-upg-100-list/my-upg-100-list.js","embed":{"iframe":"\u003ciframe 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>\u003c/iframe>","script":"\u003cscript src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-/1oZAkM+5gTg7nSy5yO4Dbd2CA8OXmIW7dqCVDiYgAnWy5++JNeu6JtdzTEYtqmV\" crossorigin=\"anonymous\">\u003c/script>\n\u003cmy-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/my-upg-100-list-map>"}},{"profile":"upg-100-mfe","file":"upg-100-mfe.vue","description":"","element":"my-upg-100-list-map","iframeUrl":"./my-upg-100-list/upg-100-mfe/index.html","scriptUrl":"./my-upg-100-list/my-upg-100-list.js","embed":{"iframe":"\u003ciframe 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>\u003c/iframe>","script":"\u003cscript src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-/1oZAkM+5gTg7nSy5yO4Dbd2CA8OXmIW7dqCVDiYgAnWy5++JNeu6JtdzTEYtqmV\" crossorigin=\"anonymous\">\u003c/script>\n\u003cmy-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/my-upg-100-list-map>"}}]}]}; - + - + - + + window.FORGE.manifest = {"schema":"doxa-maps-manifest@1","generatedFrom":"plugin-designs/*/*.vue","embedBase":".","bundleCount":10,"profileCount":19,"bundles":[{"bundle":"research-dashboard","scriptUrl":"./research-dashboard/research-dashboard.js","output":"doxa-maps/research-dashboard/research-dashboard.js","integrity":"sha384-iiwu9NS8aQ4JKQ2ksPF487DAN3ktlPs/3fTdkoqe/opPMCY91PqnT354dLhMBlM2","elements":["research-dashboard-map"],"profiles":[{"profile":"research-dashboard","file":"research-dashboard.vue","description":"Coordinated-Multiple-Views research dashboard for 10/40-window UPGs (live pray.doxa.life): docked facet rail + map-as-hero + linked table/charts, brushing-and-linking over one in-memory store.","element":"research-dashboard-map","iframeUrl":"./research-dashboard/research-dashboard/index.html","scriptUrl":"./research-dashboard/research-dashboard.js","embed":{"iframe":"\u003ciframe src=\"./research-dashboard/research-dashboard/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./research-dashboard/research-dashboard.js\" integrity=\"sha384-iiwu9NS8aQ4JKQ2ksPF487DAN3ktlPs/3fTdkoqe/opPMCY91PqnT354dLhMBlM2\" crossorigin=\"anonymous\">\u003c/script>\n\u003cresearch-dashboard-map profile-config='{\"profile\":\"research-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/research-dashboard-map>"},"bundle":"research-dashboard"}]}]}; - + + window.FORGE.manifest = {"schema":"doxa-maps-manifest@1","generatedFrom":"plugin-designs/*/*.vue","embedBase":".","bundleCount":10,"profileCount":19,"bundles":[{"bundle":"template-bundle","scriptUrl":"./template-bundle/template-bundle.js","output":"doxa-maps/template-bundle/template-bundle.js","integrity":"sha384-FwwULnUkiVHcc88AwL8QA5fGsZRqLnQHPqXmug5hxtCyTvC8VIdmcnXDJ+KvhHpm","elements":["template-bundle"],"profiles":[{"profile":"template-map-a","file":"template-map-a.vue","description":"Minimal example profile A — a colored placeholder, no Mapbox or data.","element":"template-bundle","iframeUrl":"./template-bundle/template-map-a/index.html","scriptUrl":"./template-bundle/template-bundle.js","embed":{"iframe":"\u003ciframe src=\"./template-bundle/template-map-a/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./template-bundle/template-bundle.js\" integrity=\"sha384-FwwULnUkiVHcc88AwL8QA5fGsZRqLnQHPqXmug5hxtCyTvC8VIdmcnXDJ+KvhHpm\" crossorigin=\"anonymous\">\u003c/script>\n\u003ctemplate-bundle profile-config='{\"profile\":\"template-map-a\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/template-bundle>"},"bundle":"template-bundle"},{"profile":"template-map-b","file":"template-map-b.vue","description":"Minimal example profile B — a second profile in the same bundle.","element":"template-bundle","iframeUrl":"./template-bundle/template-map-b/index.html","scriptUrl":"./template-bundle/template-bundle.js","embed":{"iframe":"\u003ciframe src=\"./template-bundle/template-map-b/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript src=\"./template-bundle/template-bundle.js\" integrity=\"sha384-FwwULnUkiVHcc88AwL8QA5fGsZRqLnQHPqXmug5hxtCyTvC8VIdmcnXDJ+KvhHpm\" crossorigin=\"anonymous\">\u003c/script>\n\u003ctemplate-bundle profile-config='{\"profile\":\"template-map-b\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/template-bundle>"},"bundle":"template-bundle"}]}]}; - + - +