Complete app-profiles->plugin-designs sweep across comments/docs; build green; layouts kept at root (load-bearing)
This commit is contained in:
parent
1a3925c6a6
commit
2d64e21fbf
136 changed files with 393 additions and 641 deletions
|
|
@ -7,7 +7,7 @@ lookups in [docs/reference.md](./docs/reference.md), depth in
|
|||
## The mental model — three zones
|
||||
|
||||
```
|
||||
library/ (@map) app-profiles/<map>/ internal/
|
||||
library/ (@map) plugin-designs/<map>/ 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.
|
||||
|
|
|
|||
|
|
@ -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/<your-map>/`.
|
||||
- **One repository for all your maps.** Each map is an *application profile* in `plugin-designs/<your-map>/`.
|
||||
- **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/<map>/ becomes
|
||||
bun run build # the build step: every plugin-designs/<map>/ becomes
|
||||
# public/doxa-maps/<map>/<map>.js + a standalone index.html per map
|
||||
|
||||
bun run serve # preview the BUILT tree (the SPA shell + every embed page)
|
||||
|
|
|
|||
|
|
@ -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/<your-map>/src/colors/<name>.js`.
|
||||
1. In your bundle folder, create `plugin-designs/<your-map>/src/colors/<name>.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
|
||||
|
|
|
|||
|
|
@ -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/<your-map>/`).
|
||||
> **Not reused → it lives in your app-profile folder** (`plugin-designs/<your-map>/`).
|
||||
|
||||
"**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/<bundle>/src/colors/` — and how the filename decides override-vs-add. The
|
||||
`plugin-designs/<bundle>/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).
|
||||
|
|
|
|||
|
|
@ -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/*/. -->
|
||||
<script type="module" src="./staging-main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 `<name>.js`
|
||||
in that bundle's `app-profiles/<bundle>/src/colors/`. At init, `index.js` calls
|
||||
in that bundle's `plugin-designs/<bundle>/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/<bundle>/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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 `<demo-map>` 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 `<demo-map>` 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 `<demo-map>` 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 `<demo-map>` 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 `<doxa-research-map>`. | 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 `<doxa-map>` (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 `<doxa-research-map>`. | 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 `<doxa-map>` (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 `<doxa-map>` (legacy) AND `<doxa-simple-map>` 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 `<doxa-map>` 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/<id>/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 `<doxa-map>` (legacy) AND `<doxa-simple-map>` 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 `<doxa-map>` 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/<id>/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 `<template-bundle>`. | 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 `<template-bundle>`. | 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/<name>/`. | 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/<name>/<bundle>/<bundle>.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/<name>/<bundle>/<bundle>.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/<name>` 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 `<script>` in the built HTML via `node --check`. | Only matches bare `<script>` (no attributes), checked as classic scripts — import.meta or a literal `</script>` 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=<name>` (`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=<name>` (`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. |
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ i18n/
|
|||
|
||||
## How it integrates with the component library
|
||||
|
||||
The app-profile entry (`app-profiles/<name>/index.js`) does `app.use(createAppI18n())` alongside
|
||||
The app-profile entry (`plugin-designs/<name>/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 `<html lang>`); the
|
||||
fallback chain is `<detected> → en`. Because area files are namespaced by filename, a component
|
||||
|
|
|
|||
|
|
@ -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/<name>/` → one
|
||||
> **Vocabulary.** A **bundle** is one folder under `plugin-designs/<name>/` → one
|
||||
> `<name>.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
|
||||
|
|
|
|||
|
|
@ -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.**
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# What doxa-maps is
|
||||
|
||||
A portable bundler for embeddable maps. One shared `library/`, many `app-profiles/<map>/`
|
||||
A portable bundler for embeddable maps. One shared `library/`, many `plugin-designs/<map>/`
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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/<your-map>/`, never touch `internal/`.**
|
||||
`plugin-designs/<your-map>/`, 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 `<name>.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/<your-map>/src/colors/<name>.js` (copy `template-bundle/src/colors/example-mode.js`
|
||||
`plugin-designs/<your-map>/src/colors/<name>.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/<map>/` = the sandbox for **one**
|
||||
> **reuse via the `@map/…` alias, never edit**. `plugin-designs/<map>/` = 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/<the-map>/`.
|
||||
> 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/<the-map>/`.
|
||||
> 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.
|
||||
|
|
|
|||
|
|
@ -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/<name>/` | — | yours | …freely; nothing here leaks to other maps |
|
||||
| **Your app-profile** | `plugin-designs/<name>/` | — | 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 `<doxa-map>`: 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/<bundle>/` that defines a whole map app — its main component; the settings on the
|
||||
flat `.vue` in `plugin-designs/<bundle>/` 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)
|
||||
│ ├── <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/<bundle>/ built bundles + manifest.json + staging HTML
|
||||
│ └── assets/ posters & data the maps fetch at runtime
|
||||
├── vite.config.js ← multi-entry: discovers app-profiles/<name>/ folders
|
||||
├── vite.config.js ← multi-entry: discovers plugin-designs/<name>/ 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 <file>` 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 <page> --tag <element>` = 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 <file>`; exit 1 on any failure — proves REQ-1 ("links work wherever the tree is hosted"). |
|
||||
| `bun run check:public-hygiene [<dir>] [--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 <dest> [--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=<name>`). |
|
||||
| `bun run build:watch` | Rebuilds into `public/` on every change (integration loop). |
|
||||
| `bun run clean` | Wipes + recreates the `public/<name>/` 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/<bundle>/index.js` instead of adding the built IIFE), the live
|
||||
only in the loader (it imports `/plugin-designs/<bundle>/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=<name>`, 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/<bundle>/<bundle>.js` |
|
||||
| Build just one | `BUNDLE=<name> bun run build:bundle` |
|
||||
| Preview in isolation | `bun run dev` → `http://localhost:5173/app-profiles/<name>/index.html` |
|
||||
| Preview in isolation | `bun run dev` → `http://localhost:5173/plugin-designs/<name>/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` |
|
||||
|
|
|
|||
|
|
@ -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-map>/`** | Your **sandbox**. Your one map lives here: its screens, colors, API wiring. | **Yes — build anything.** Nothing here leaks to another map. |
|
||||
| **`plugin-designs/<your-map>/`** | 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/<your-map>/`, never touch `internal/`.**
|
||||
One line: **reuse `library/`, build in `plugin-designs/<your-map>/`, never touch `internal/`.**
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -101,14 +101,14 @@ One line: **reuse `library/`, build in `app-profiles/<your-map>/`, 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/<your-map>/src/colors/`. Copy `example-mode.js`, rename it, change the hex
|
||||
> `plugin-designs/<your-map>/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.
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@
|
|||
<main id="showcase"></main>
|
||||
|
||||
<div class="foot">
|
||||
Live proof of the bundler · bundles auto-detected from <code>app-profiles/*/</code> · maps lazy-mounted, WebGL-context-safe.
|
||||
Live proof of the bundler · bundles auto-detected from <code>plugin-designs/*/</code> · maps lazy-mounted, WebGL-context-safe.
|
||||
<span class="scr">“Write the vision, and make it plain … that he may run that readeth it.” — Habakkuk 2:2</span>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
* web-root, written by `bun run gallery` (last step of
|
||||
* `bun run build`) from public/<tree>/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), <tree>/ (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/<bundle>/index.js, an ES module Vite serves) instead of adding an
|
||||
* (scriptUrl = /plugin-designs/<bundle>/index.js, an ES module Vite serves) instead of adding an
|
||||
* IIFE script tag; the live model is fetched from the dev route (<tree> = __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`<script>
|
|||
how: 'The bundle index.js turns the shared ProfileLoader into an element class with defineCustomElement and registers the tag once with customElements.define.',
|
||||
ex: '<doxa-map profile-config='{"profile":"doxa-simple-map"}'></doxa-map>' },
|
||||
{ title: 'App profile (mounting file)',
|
||||
def: 'One flat .vue file in app-profiles/<bundle>/ that defines a whole map app — the main component of that map. The settings written 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 on a page is an instance, and the app profile is the screen that instance loads.',
|
||||
how: 'Most repos need one such main file per app. doxa-maps builds hundreds of maps from one shared library, so every map gets its own app profile — the folder app-profiles/ is where they sit. The five kinds on this ladder are the five shapes those files come in.',
|
||||
ex: 'app-profiles/doxa-simple-map/doxa-simple-map.vue' }
|
||||
def: 'One flat .vue file in plugin-designs/<bundle>/ that defines a whole map app — the main component of that map. The settings written 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 on a page is an instance, and the app profile is the screen that instance loads.',
|
||||
how: 'Most repos need one such main file per app. doxa-maps builds hundreds of maps from one shared library, so every map gets its own app profile — the folder plugin-designs/ is where they sit. The five kinds on this ladder are the five shapes those files come in.',
|
||||
ex: 'plugin-designs/doxa-simple-map/doxa-simple-map.vue' }
|
||||
];
|
||||
var LADDER = [
|
||||
{ label: 'single-element', tier: 4, title: 'Single element', aka: 'single',
|
||||
|
|
|
|||
|
|
@ -87,8 +87,8 @@ alias; **build your own custom code in your app-profile's \`src/\` folder.** Do
|
|||
anything listed here unless you know what you're doing — these are shared across every map,
|
||||
and a change here can break other maps.
|
||||
|
||||
- **Three zones:** \`library/\` (this menu — reuse, don't edit) · \`app-profiles/<map>/\` (your sandbox — build anything) · \`internal/\` (the build machine — never touch).
|
||||
- **To make a new map:** copy \`app-profiles/template-bundle/\`, read its README, list your requirements, pick from below.
|
||||
- **Three zones:** \`library/\` (this menu — reuse, don't edit) · \`plugin-designs/<map>/\` (your sandbox — build anything) · \`internal/\` (the build machine — never touch).
|
||||
- **To make a new map:** copy \`plugin-designs/template-bundle/\`, read its README, list your requirements, pick from below.
|
||||
`;
|
||||
|
||||
md += section('Components (reusable UI)', 'components', { recursive: true, ext: '.vue' }, '@map/components');
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* generate-tree.mjs — build the self-contained doxa-maps/ output tree.
|
||||
*
|
||||
* Runs AFTER the per-bundle vite builds (which emit app/doxa-maps/<bundle>/<bundle>.js).
|
||||
* It scans app-profiles/<bundle>/*.vue, pulls the OPTIONAL JSDoc header tags from the
|
||||
* It scans plugin-designs/<bundle>/*.vue, pulls the OPTIONAL JSDoc header tags from the
|
||||
* top of each profile, reads the custom-element tag(s) each bundle registers in its
|
||||
* index.js, then WRITES, into the doxa-maps/ tree:
|
||||
*
|
||||
|
|
@ -628,7 +628,7 @@ function readInstances(dir, profileName, bundle, tag) {
|
|||
})
|
||||
}
|
||||
|
||||
// ── Scan app-profiles → bundle records ───────────────────────────────────────
|
||||
// ── Scan plugin-designs → bundle records ───────────────────────────────────────
|
||||
function scanBundles() {
|
||||
if (!existsSync(APP_PROFILES)) {
|
||||
console.error(`[generate-tree] no plugin-designs/ at ${APP_PROFILES}`)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
* Phone widths (< 600) tap the sheet's drag strip first so the rows are visible.
|
||||
* Site: --url http://localhost:3001/pray --tag doxa-map
|
||||
* --url http://localhost:3001/research/ --tag doxa-research-map
|
||||
* Plugin gallery dev server: --url http://localhost:5174/app-profiles/doxa-simple-map/index.html
|
||||
* Plugin gallery dev server: --url http://localhost:5174/plugin-designs/doxa-simple-map/index.html
|
||||
*/
|
||||
import { launch, TELEMETRY } from './lib/browser.mjs'
|
||||
const args = process.argv.slice(2); const argOf = (k, d) => { const i = args.indexOf(k); return i >= 0 ? args[i + 1] : d }
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
* shadow roots). Every observation is bucketed by the phase it STARTED in.
|
||||
* --profile (Chromium): CDP Profiler sampled at 0.5 ms during the idle phase, aggregated
|
||||
* to self/total ms per function@file:line; the top frames are printed (all, and app-only:
|
||||
* library/ app-profiles/ internal/) and saved in the report. Line numbers are those of
|
||||
* library/ plugin-designs/ internal/) and saved in the report. Line numbers are those of
|
||||
* the Vite-served module (.js files keep their lines; .vue compiled output does not).
|
||||
* Browser: Chromium by DEFAULT here (Playwright's bundled build — never Google Chrome) —
|
||||
* LoAF, the Violation log and the Profiler are Chromium-only; --browser firefox still
|
||||
|
|
@ -80,7 +80,7 @@ const JSON_OUT = argOf('--json', '')
|
|||
const [VW, VH] = String(argOf('--viewport', '1440x900')).split('x').map(Number)
|
||||
const VIEWPORT = { width: VW || 1440, height: VH || 900 }
|
||||
const VIOLATION_RE = /requestAnimationFrame/i // applied to Violation-source log entries + console text
|
||||
const APP_URL_RE = /\/(library|app-profiles|internal)\//
|
||||
const APP_URL_RE = /\/(library|plugin-designs|internal)\//
|
||||
|
||||
// ── targets ──────────────────────────────────────────────────────────────────
|
||||
if (TAGS.length > 1 && TAGS.length !== URLS.length) { console.error(`[${TOOL}] ${TAGS.length} --tag for ${URLS.length} --url — give one per --url, one for all, or none`); process.exit(2) }
|
||||
|
|
@ -199,7 +199,7 @@ function aggregateProfile(profile) {
|
|||
const meta = /^\((program|idle|garbage collector|root)\)$/
|
||||
const byGroup = {}
|
||||
for (const [k, ms] of self) {
|
||||
const g = meta.test(k) ? k : /mapbox-gl|maplibre-gl/.test(k) ? 'mapbox-gl' : APP_URL_RE.test(k) ? 'app (library/app-profiles/internal)' : /\/node_modules\/|\/@vite\/|\/@fs\//.test(k) ? 'other node_modules' : 'other'
|
||||
const g = meta.test(k) ? k : /mapbox-gl|maplibre-gl/.test(k) ? 'mapbox-gl' : APP_URL_RE.test(k) ? 'app (library/plugin-designs/internal)' : /\/node_modules\/|\/@vite\/|\/@fs\//.test(k) ? 'other node_modules' : 'other'
|
||||
byGroup[g] = Math.round((byGroup[g] || 0) + ms)
|
||||
}
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
* [--no-select] skip the people-group selection and check the LEGEND-TAB
|
||||
* free-drag contract instead (sheet stays where released,
|
||||
* pull-to-bottom collapses, strip tap re-opens) + overlap audit
|
||||
* Defaults: --url http://localhost:5174/app-profiles/doxa-simple-map/index.html
|
||||
* Defaults: --url http://localhost:5174/plugin-designs/doxa-simple-map/index.html
|
||||
* --tag doxa-map --nth 0. NOTE the standalone dev page ignores ?profile= (its tabs
|
||||
* are per-bundle, read from localStorage) and renders three stacked <doxa-map>
|
||||
* slots (engagement / prayer / adoption): pick one with --instance dev-prayer.
|
||||
|
|
@ -65,7 +65,7 @@ import { launch, TELEMETRY } from './lib/browser.mjs'
|
|||
const TOOL = 'mobile-sheet-probe'
|
||||
const args = process.argv.slice(2)
|
||||
const argOf = (k, d) => { const i = args.indexOf(k); return i >= 0 ? args[i + 1] : d }
|
||||
const URL = String(argOf('--url', 'http://localhost:5174/app-profiles/doxa-simple-map/index.html'))
|
||||
const URL = String(argOf('--url', 'http://localhost:5174/plugin-designs/doxa-simple-map/index.html'))
|
||||
const TAG = argOf('--tag', 'doxa-map')
|
||||
const NTH = Number(argOf('--nth', 0))
|
||||
const INSTANCE = argOf('--instance', '')
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import { readBuildConfig, renderGallery } from '../generate-gallery.mjs'
|
|||
|
||||
// ─── DEV plugin gallery ──────────────────────────────────────────────────────
|
||||
// `bun x vite` (dev) serves the plugin gallery at `/` — the same page `bun run build`
|
||||
// writes to public/index.html — over every profile DISCOVERED across app-profiles/*/.
|
||||
// writes to public/index.html — over every profile DISCOVERED across plugin-designs/*/.
|
||||
// Choosing a map lazy-imports that bundle's dev entry (registering its custom element)
|
||||
// and mounts it, with the dev Mapbox token (VITE_MAPBOX_TOKEN) as the baked rung of
|
||||
// the token ladder so maps just work. Dev-only (apply:'serve'); the build is untouched.
|
||||
|
|
@ -27,7 +27,7 @@ import { readBuildConfig, renderGallery } from '../generate-gallery.mjs'
|
|||
// and `'/boundaries/*.geojson'`. publicDir is false ON PURPOSE (a bundle build must never
|
||||
// inline public/), so dev has to serve the asset tree explicitly, the way the built site
|
||||
// does: public/assets first (exactly what generate-tree ships), else the per-bundle source
|
||||
// of truth app-profiles/<bundle>/data/<rel> when public/ has been cleaned.
|
||||
// of truth plugin-designs/<bundle>/data/<rel> when public/ has been cleaned.
|
||||
const DEV_ASSET_MIME = {
|
||||
'.json': 'application/json; charset=utf-8', '.geojson': 'application/geo+json; charset=utf-8',
|
||||
'.js': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8', '.csv': 'text/csv; charset=utf-8',
|
||||
|
|
@ -39,7 +39,7 @@ export function resolveDevAsset(rel, root = REPO_ROOT) {
|
|||
const isFile = (p) => existsSync(p) && statSync(p).isFile()
|
||||
const shipped = resolve(root, 'public', 'assets', rel)
|
||||
if (isFile(shipped)) return shipped
|
||||
const profiles = resolve(root, 'app-profiles')
|
||||
const profiles = resolve(root, 'plugin-designs')
|
||||
for (const name of readdirSync(profiles)) {
|
||||
if (name.startsWith('_') || name.startsWith('.')) continue
|
||||
const cand = resolve(profiles, name, 'data', rel)
|
||||
|
|
@ -62,7 +62,7 @@ export function galleryModel(reg) {
|
|||
const iframe = (u) => `<iframe src="${u}?utm_source=YOUR_SOURCE" width="100%" height="600" style="border:none;border-radius:12px;" allowfullscreen></iframe>`
|
||||
const link = (...parts) => '/#/' + parts.map(encodeURIComponent).join('/')
|
||||
const bundles = [...reg].sort((a, b) => a.bundle.localeCompare(b.bundle)).map((b) => {
|
||||
const scriptUrl = `/app-profiles/${b.bundle}/index.js`
|
||||
const scriptUrl = `/plugin-designs/${b.bundle}/index.js`
|
||||
const presets = new Map() // profile → its preset instances (empty for every other kind)
|
||||
for (const it of b.items) {
|
||||
if (!presets.has(it.profile)) presets.set(it.profile, [])
|
||||
|
|
@ -89,7 +89,7 @@ export function galleryModel(reg) {
|
|||
return { bundle: b.bundle, scriptUrl, integrity: '', elements: [b.tag], profiles }
|
||||
})
|
||||
return {
|
||||
schema: 'doxa-maps-manifest@1', generatedFrom: 'app-profiles/*/*.vue (dev discovery)', embedBase: '.',
|
||||
schema: 'doxa-maps-manifest@1', generatedFrom: 'plugin-designs/*/*.vue (dev discovery)', embedBase: '.',
|
||||
bundleCount: bundles.length, profileCount: bundles.reduce((n, b) => n + b.profiles.length, 0), bundles,
|
||||
}
|
||||
}
|
||||
|
|
@ -126,7 +126,7 @@ export function autoSpaDevPlugin(token = '') {
|
|||
try { rel = decodeURIComponent(path.slice('/assets/'.length)) } catch { rel = '' }
|
||||
if (!rel || rel.split('/').includes('..')) { res.statusCode = 400; return res.end('bad asset path') }
|
||||
const file = resolveDevAsset(rel)
|
||||
if (!file) return json(res, { error: 'asset not found', asset: rel, looked: ['public/assets/', 'app-profiles/*/data/'] }, 404)
|
||||
if (!file) return json(res, { error: 'asset not found', asset: rel, looked: ['public/assets/', 'plugin-designs/*/data/'] }, 404)
|
||||
res.statusCode = 200
|
||||
res.setHeader('Content-Type', DEV_ASSET_MIME[extname(file).toLowerCase()] || 'application/octet-stream')
|
||||
res.setHeader('Cache-Control', 'no-cache')
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* discover.mjs — the auto-generated SPA's registry: every bundle under app-profiles/,
|
||||
* discover.mjs — the auto-generated SPA's registry: every bundle under plugin-designs/,
|
||||
* every profile inside it, classified into a composition tier.
|
||||
*
|
||||
* single → parameterized → monolithic → nested-elements-mfe → nested-mfe-spa
|
||||
|
|
@ -58,7 +58,7 @@ export function detectProfileType(dir, profile, ownTag, allTags, hasInstances) {
|
|||
}
|
||||
|
||||
export function discoverSpaRegistry(root = REPO_ROOT) {
|
||||
const APP_DIR = resolve(root, 'app-profiles')
|
||||
const APP_DIR = resolve(root, 'plugin-designs')
|
||||
const bundles = []
|
||||
if (!existsSync(APP_DIR)) return bundles
|
||||
const dirs = readdirSync(APP_DIR).sort().filter(n => {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
* - Token UX: resolve ?tk= → localStorage → none. If none, show a token bar
|
||||
* (paste once, stored locally in the browser, never in files/links unless you
|
||||
* use ?tk= on purpose). Maps must Just Work after one paste.
|
||||
* - Auto-generated: whatever app-profiles exist, the tree detects and shows them.
|
||||
* - Auto-generated: whatever plugin-designs exist, the tree detects and shows them.
|
||||
*
|
||||
* file:// contract: classic script, manifest inlined at build (window.FORGE.manifest),
|
||||
* fetch only as dev fallback. No import/export. The DEV-HMR block at the bottom is
|
||||
|
|
@ -96,7 +96,7 @@ function buildFlat() {
|
|||
kind: kind === 'nested' ? 'nested' : 'profile',
|
||||
label: p.profile,
|
||||
sub: p.description || (kind === 'nested' ? 'embeds child web components' : 'one web component = one map'),
|
||||
edit: 'app-profiles/' + b.bundle + '/' + (p.file || p.profile + '.vue'),
|
||||
edit: 'plugin-designs/' + b.bundle + '/' + (p.file || p.profile + '.vue'),
|
||||
href: treeHref(b.bundle + '/' + p.profile + '/index.html'),
|
||||
bundle: b.bundle, element: p.element || (b.elements || [])[0], gem: b.__gem,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -21,13 +21,13 @@
|
|||
// language on every map as the `locale` PROP (profile-config.locale) and re-mounts.
|
||||
|
||||
// ── Discovery ───────────────────────────────────────────────────────────────────────────────
|
||||
const indexRaw = import.meta.glob('/app-profiles/*/index.js', { query: '?raw', import: 'default', eager: true })
|
||||
const htmlRaw = import.meta.glob('/app-profiles/*/index.html', { query: '?raw', import: 'default', eager: true })
|
||||
const bundleLoaders = import.meta.glob('/app-profiles/*/index.js') // lazy: importing registers the element
|
||||
const profileFiles = import.meta.glob('/app-profiles/*/*.vue')
|
||||
const indexRaw = import.meta.glob('/plugin-designs/*/index.js', { query: '?raw', import: 'default', eager: true })
|
||||
const htmlRaw = import.meta.glob('/plugin-designs/*/index.html', { query: '?raw', import: 'default', eager: true })
|
||||
const bundleLoaders = import.meta.glob('/plugin-designs/*/index.js') // lazy: importing registers the element
|
||||
const profileFiles = import.meta.glob('/plugin-designs/*/*.vue')
|
||||
|
||||
const TOKEN = import.meta.env.VITE_MAPBOX_TOKEN || ''
|
||||
const nameOf = (p) => p.split('/app-profiles/')[1].split('/')[0]
|
||||
const nameOf = (p) => p.split('/plugin-designs/')[1].split('/')[0]
|
||||
|
||||
function tagsOf(src) {
|
||||
const re = /customElements\.define\(\s*['"]([^'"]+)['"]/g
|
||||
|
|
@ -102,7 +102,7 @@ function slotsFor(b) {
|
|||
const imported = {}
|
||||
function loadBundle(name) {
|
||||
if (!imported[name]) {
|
||||
const key = Object.keys(bundleLoaders).find((k) => k.includes('/app-profiles/' + name + '/index.js'))
|
||||
const key = Object.keys(bundleLoaders).find((k) => k.includes('/plugin-designs/' + name + '/index.js'))
|
||||
imported[name] = key ? bundleLoaders[key]() : Promise.reject(new Error('bundle loader not found: ' + name))
|
||||
}
|
||||
return imported[name]
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
// useShadowStyles(`…`). Those literals ship verbatim inside the built IIFE, so any
|
||||
// CSS block comment written in them would reach the public drop-in as dead weight.
|
||||
// This build-only plugin rewrites the source of every .vue and .js file under
|
||||
// library/ and app-profiles/ before the Vue compiler sees it (enforce: 'pre'):
|
||||
// library/ and plugin-designs/ before the Vue compiler sees it (enforce: 'pre'):
|
||||
// inside each such literal it removes the block comments and collapses the blank
|
||||
// lines they leave behind. Nothing outside the literal changes, and dev mode is
|
||||
// untouched so the comments stay readable while working.
|
||||
|
|
@ -17,7 +17,7 @@ import { resolve, dirname, sep } from 'node:path'
|
|||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const SOURCE_ROOTS = ['library', 'app-profiles'].map(d => resolve(ROOT, d) + sep)
|
||||
const SOURCE_ROOTS = ['library', 'plugin-designs'].map(d => resolve(ROOT, d) + sep)
|
||||
const TAG = '[strip-shadow-css-comments]'
|
||||
|
||||
// The call site: `useShadowStyles(` then optional whitespace then the opening backtick.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# layouts/ — app wrappers
|
||||
|
||||
Each folder here is an **app wrapper**: a host shell that composes doxa-maps app-profiles (the
|
||||
Each folder here is an **app wrapper**: a host shell that composes doxa-maps plugin-designs (the
|
||||
independently-built custom elements) into one application.
|
||||
|
||||
```
|
||||
|
|
|
|||
|
|
@ -770,7 +770,7 @@ function label(id){
|
|||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════
|
||||
PROFILE REGISTRY — which app-profiles are wired to a real bundle.
|
||||
PROFILE REGISTRY — which plugin-designs are wired to a real bundle.
|
||||
`tag` the custom element the bundle registers
|
||||
`src` the built bundle (self-contained, relative → drop-portable)
|
||||
`config` the profile-config JSON the element takes
|
||||
|
|
@ -782,7 +782,7 @@ const PROFILES = {
|
|||
// which was architecturally wrong — the research map is a 6-tab research tool
|
||||
// and belongs under the Research section, not Prayer Mobilization.
|
||||
// simple-map ships three named single-tab instances (prayer / engagement /
|
||||
// adoption) declared in app-profiles/doxa-simple-map/simple-map.instances.json.
|
||||
// adoption) declared in plugin-designs/doxa-simple-map/simple-map.instances.json.
|
||||
// The 'prayer' one is titled "Prayer Map" and is the right map for this slot.
|
||||
// NOTE its element tag is <doxa-map>, NOT <doxa-simple-map> — the bundle
|
||||
// registers both, and the generated instance pages use <doxa-map>. Config is
|
||||
|
|
@ -796,7 +796,7 @@ const PROFILES = {
|
|||
// ⚠️ colorStrategy MUST be 'prayerProgress', NOT 'prayer'.
|
||||
// The strategy registry derives its keys from FILENAMES via import.meta.glob:
|
||||
// library/colors/prayer-progress.js -> 'prayerProgress'. There is no prayer.js
|
||||
// anywhere (nor a profile-local one in app-profiles/doxa-simple-map/src/colors/),
|
||||
// anywhere (nor a profile-local one in plugin-designs/doxa-simple-map/src/colors/),
|
||||
// so 'prayer' does not resolve — and getColorStrategy() SILENTLY FALLS BACK to
|
||||
// languageFamily. No error, just a completely different palette, which is why
|
||||
// this map lost the white-ringed "glowing" prayer pins.
|
||||
|
|
@ -838,7 +838,7 @@ const PROFILES = {
|
|||
// is what turns it into a real app.
|
||||
//
|
||||
// Tag + profile names come from the bundle itself, NOT guessed:
|
||||
// folder app-profiles/country-posters/ -> src below
|
||||
// folder plugin-designs/country-posters/ -> src below
|
||||
// element <country-posters-map> (index.js customElements.define)
|
||||
// profiles countries-map.vue / country-poster-wall.vue
|
||||
// The profile value is the .vue FILENAME, which is why 'countries-map' is right
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ const instanceId = computed(() =>
|
|||
)
|
||||
|
||||
// ─── Profile Registry — provided by bundle entry ────────────────────────────
|
||||
// The bundle entry (app-profiles/<bundle>/index.js) MUST run
|
||||
// The bundle entry (plugin-designs/<bundle>/index.js) MUST run
|
||||
// app.provide('profileModules', import.meta.glob('./*.vue'))
|
||||
// because import.meta.glob cannot cross package boundaries — it must be
|
||||
// evaluated by the bundle that owns the profile .vue files.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# library/ (`@map`) — the shared map template
|
||||
|
||||
**The other half of the architecture** (see [`../app-profiles/README.md`](../app-profiles/README.md)):
|
||||
every map in `app-profiles/<your-map>/` composes the reusable, parameterized pieces that live here.
|
||||
**The other half of the architecture** (see [`../plugin-designs/README.md`](../plugin-designs/README.md)):
|
||||
every map in `plugin-designs/<your-map>/` composes the reusable, parameterized pieces that live here.
|
||||
Fix something here and **every map benefits** on the next build; break something here and every map
|
||||
breaks — read [`components/WARNING.md`](./components/WARNING.md) before touching shared pieces.
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ progress, religion, …). Drop a `<name>.js` in and it's auto-discovered; no reg
|
|||
│ _registry.js (the scanner)│ └───────────────────────┘
|
||||
└──────────────────────────┘
|
||||
shared set a single map can add/override its own in
|
||||
app-profiles/<map>/src/colors/ (that map only)
|
||||
plugin-designs/<map>/src/colors/ (that map only)
|
||||
```
|
||||
|
||||
- **Shared here** = reused by every map. **Your map's own** color → put it in your bundle's
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Each strategy lives in its own file in this folder (one strategy = one file).
|
||||
* Strategies are discovered automatically at build time via Vite's
|
||||
* `import.meta.glob` — the SAME pattern app-profiles use to auto-discover
|
||||
* `import.meta.glob` — the SAME pattern plugin-designs use to auto-discover
|
||||
* bundles. There is NO manual registration step.
|
||||
*
|
||||
* Add a new strategy:
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
# ⚠️ Read before you touch these components
|
||||
|
||||
**These are the SHARED, REUSABLE components — every map in `app-profiles/` depends on them.**
|
||||
**These are the SHARED, REUSABLE components — every map in `plugin-designs/` depends on them.**
|
||||
|
||||
A change here does not affect one map. It ripples to **all** of them. So:
|
||||
|
||||
- **Prototyping or experimenting? Don't edit these.** Put your custom, one-off, not-yet-proven component
|
||||
inside your own map's folder (`app-profiles/<your-map>/`), where it can be as messy as you need and
|
||||
inside your own map's folder (`plugin-designs/<your-map>/`), where it can be as messy as you need and
|
||||
breaks nothing else.
|
||||
- **Only proven, parameterized, reusable components graduate into this library.** If a component has hard-coded
|
||||
values, assumptions about one specific map, or hasn't been used by more than one map yet — it is **not**
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import { getColorStrategy, COLOR_MODES } from '../../colors/_registry.js'
|
|||
// (inside the builder, at call time) so registration has already run — a
|
||||
// module-load-time read could see an empty palette.
|
||||
const doxaRegionColors = () => getColorStrategy(COLOR_MODES.DOXA_REGION)?.palette ?? {}
|
||||
// resource is likewise research-only (app-profiles/doxa-research-map/src/colors/
|
||||
// resource is likewise research-only (plugin-designs/doxa-research-map/src/colors/
|
||||
// resource.js), registry-resolved. Lazy + GUARDED on propertyKey — getColorStrategy
|
||||
// falls back to language-family for unregistered modes, and that palette must not
|
||||
// masquerade as resource colors.
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ import { computed } from 'vue'
|
|||
// Single source of truth — pins paint with this same palette, keyed by ROP1 code.
|
||||
// Resolving the SAME strategy the pins use keeps legend ↔ map in lock-step (was a
|
||||
// separate name-keyed palette causing pin/legend drift). affinity-block is a per-map
|
||||
// (research-only) strategy that lives in app-profiles/doxa-research-map/
|
||||
// (research-only) strategy that lives in plugin-designs/doxa-research-map/
|
||||
// src/colors/affinity-block.js and is resolved via the registry (registered by
|
||||
// the research bundle's local src/colors glob). Read LAZILY (at call time
|
||||
// inside blocColor — this composable is only invoked by research surfaces) so
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ const COLUMNS_SIMPLE = [] // e.g. affinity-blocs — no numeric columns
|
|||
|
||||
// ─── Resource palette/names (research-only strategy) ──────────────────────────
|
||||
// resource is a per-map (research-only) strategy that lives in
|
||||
// app-profiles/doxa-research-map/src/colors/resource.js and is resolved via
|
||||
// plugin-designs/doxa-research-map/src/colors/resource.js and is resolved via
|
||||
// the registry (registered by the research bundle's local src/colors glob).
|
||||
// Read LAZILY (inside the items computed, at call time) so registration has already
|
||||
// run, and GUARDED on propertyKey — getColorStrategy silently falls back to
|
||||
|
|
@ -42,7 +42,7 @@ function resourceStrategy() {
|
|||
|
||||
// ─── Region / Bloc palettes (registry strategies — single source of truth) ───
|
||||
// doxa-region and affinity-block are PER-MAP (research-only) strategies that
|
||||
// live in app-profiles/doxa-research-map/src/colors/ and register via the
|
||||
// live in plugin-designs/doxa-research-map/src/colors/ and register via the
|
||||
// research bundle's local src/colors glob. Read LAZILY (inside the items
|
||||
// computed, at call time) so registration has already run, and GUARDED on
|
||||
// propertyKey — getColorStrategy silently falls back to language-family when a
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { nextTick, unref } from 'vue'
|
|||
|
||||
/**
|
||||
* Shared geocoder→map search logic (kind-switch + Mapbox filter builder + the
|
||||
* common legend-tab tail). One copy for all map app-profiles AND the future
|
||||
* common legend-tab tail). One copy for all map plugin-designs AND the future
|
||||
* dashboard — the kind-switch / per-kind property map / filter-expression
|
||||
* builder live HERE and nowhere else.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
// bbox, and which sheet size it takes.
|
||||
//
|
||||
// node emit-countries.mjs
|
||||
// -> ../../app-profiles/country-posters/data/countries.json
|
||||
// -> ../../plugin-designs/country-posters/data/countries.json
|
||||
//
|
||||
// SHEET LADDER (design note 2026-08-01) — the count selects the SHEET SIZE, it does not
|
||||
// exclude a country:
|
||||
|
|
|
|||
|
|
@ -26,14 +26,14 @@ import { dirname, join, resolve } from 'node:path';
|
|||
import { fetchGrid, bestZoom } from './osm-basemap.mjs';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
// The profile data lives in THIS repo — library/scripts/posters -> app-profiles.
|
||||
// The profile data lives in THIS repo — library/scripts/posters -> plugin-designs.
|
||||
// This generator used to sit outside doxa-maps entirely and reach in via
|
||||
// UPG100_DATA_PATH; forgetting that env var silently rendered a STALE COPY of the
|
||||
// data (that is how Nepal vanished from a poster). Now the default path is the
|
||||
// repo's own profile, so the generator cannot read anyone else's data by accident.
|
||||
const DATA = process.env.UPG100_DATA_PATH
|
||||
? resolve(process.env.UPG100_DATA_PATH)
|
||||
: resolve(__dirname, '..', '..', '..', 'app-profiles', 'my-upg-100-list', 'data');
|
||||
: resolve(__dirname, '..', '..', '..', 'plugin-designs', 'my-upg-100-list', 'data');
|
||||
const OUT = join(__dirname, 'out');
|
||||
const POSTERS = join(OUT, 'posters');
|
||||
mkdirSync(POSTERS, { recursive: true });
|
||||
|
|
@ -2520,7 +2520,7 @@ if (CITIES) {
|
|||
console.log(`[generate] CITY MODE — ${zones.length} named ASCII population centres` +
|
||||
(unresolved ? ` (${unresolved} group names did not resolve to a slug)` : ''));
|
||||
} else if (COUNTRIES) {
|
||||
const cpath = resolve(__dirname, '..', '..', '..', 'app-profiles', 'country-posters', 'data', 'countries.json');
|
||||
const cpath = resolve(__dirname, '..', '..', '..', 'plugin-designs', 'country-posters', 'data', 'countries.json');
|
||||
const cdata = JSON.parse(readFileSync(cpath, 'utf8'));
|
||||
const want = process.env.SHEET || '3x3'; // which tier to build
|
||||
zones = (cdata.countries || [])
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
the app ships.
|
||||
|
||||
python3 make-previews.py
|
||||
-> app-profiles/my-upg-100-list/assets/previews/<zone>.webp 560px card thumbnails
|
||||
-> plugin-designs/my-upg-100-list/assets/previews/<zone>.webp 560px card thumbnails
|
||||
-> public/assets/posters/<zone>.webp 1500px the download
|
||||
|
||||
WHY TWO SIZES
|
||||
|
|
@ -62,5 +62,5 @@ for p in pngs:
|
|||
fkb += os.path.getsize(fp) / 1024
|
||||
|
||||
print(f'[make-previews] {len(pngs)} posters')
|
||||
print(f' thumbnails -> app-profiles/.../assets/previews {tkb/1024:5.1f} MB (inlined in the bundle)')
|
||||
print(f' thumbnails -> plugin-designs/.../assets/previews {tkb/1024:5.1f} MB (inlined in the bundle)')
|
||||
print(f' full size -> public/assets/posters {fkb/1024:5.1f} MB (served + shipped in the APK)')
|
||||
|
|
|
|||
|
|
@ -24,14 +24,14 @@ import { fileURLToPath } from 'node:url';
|
|||
import { dirname, join, resolve } from 'node:path';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
// The profile data lives in THIS repo — library/scripts/posters -> app-profiles.
|
||||
// The profile data lives in THIS repo — library/scripts/posters -> plugin-designs.
|
||||
// This generator used to sit outside doxa-maps entirely and reach in via
|
||||
// UPG100_DATA_PATH; forgetting that env var silently rendered a STALE COPY of the
|
||||
// data (that is how Nepal vanished from a poster). Now the default path is the
|
||||
// repo's own profile, so the generator cannot read anyone else's data by accident.
|
||||
const DATA = process.env.UPG100_DATA_PATH
|
||||
? resolve(process.env.UPG100_DATA_PATH)
|
||||
: resolve(__dirname, '..', '..', 'app-profiles', 'my-upg-100-list', 'data');
|
||||
: resolve(__dirname, '..', '..', 'plugin-designs', 'my-upg-100-list', 'data');
|
||||
const OUT = join(__dirname, 'out');
|
||||
const POSTERS = join(OUT, 'posters');
|
||||
mkdirSync(POSTERS, { recursive: true });
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import { fileURLToPath } from 'node:url';
|
|||
import { dirname, join, resolve } from 'node:path';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const DATA = process.env.UPG100_DATA_PATH ? resolve(process.env.UPG100_DATA_PATH) : join(__dirname, '..', 'app-profiles', 'my-upg-100-list', 'data');
|
||||
const DATA = process.env.UPG100_DATA_PATH ? resolve(process.env.UPG100_DATA_PATH) : join(__dirname, '..', 'plugin-designs', 'my-upg-100-list', 'data');
|
||||
const OUT = join(__dirname, 'out');
|
||||
const POSTERS = join(OUT, 'posters');
|
||||
mkdirSync(POSTERS, { recursive: true });
|
||||
|
|
|
|||
|
|
@ -70,11 +70,11 @@ cp snapshots/v2-3x3-full-vision/generate.mjs generate.mjs
|
|||
|
||||
```bash
|
||||
ONLY=asia-india-uttarakhand \
|
||||
UPG100_DATA_PATH=<repo-root>/app-profiles/my-upg-100-list/data \
|
||||
UPG100_DATA_PATH=<repo-root>/plugin-designs/my-upg-100-list/data \
|
||||
OFFLINE=1 VITE_API_BASE_URL=https://pray.doxa.life FORMAT=3x3 \
|
||||
node generate.mjs
|
||||
```
|
||||
|
||||
`UPG100_DATA_PATH` is **required** — the generator's default points at a stale duplicate of
|
||||
the profile data in `app-profiles/`, which is missing Nepal (58 slugs, no
|
||||
the profile data in `plugin-designs/`, which is missing Nepal (58 slugs, no
|
||||
`adm0`). Without it Nepal silently disappears from the sheet.
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ cd <repo-root>/library/scripts/posters
|
|||
|
||||
# ONE poster
|
||||
SPARSE=1 SERIES_INDEX=1 ONLY=asia-india-uttarakhand \
|
||||
UPG100_DATA_PATH=<repo-root>/app-profiles/my-upg-100-list/data \
|
||||
UPG100_DATA_PATH=<repo-root>/plugin-designs/my-upg-100-list/data \
|
||||
OFFLINE=1 VITE_API_BASE_URL=https://pray.doxa.life FORMAT=3x3 \
|
||||
node generate.mjs
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ SPARSE=1 UPG100_DATA_PATH=... OFFLINE=1 VITE_API_BASE_URL=https://pray.doxa.life
|
|||
|---|---|
|
||||
| `SPARSE=1` | **Selects THIS template.** Without it you get the v2 full-vision layout. |
|
||||
| `FORMAT=3x3` | 1500×1500 square sheet. |
|
||||
| `UPG100_DATA_PATH` | **Required.** The generator's default points at a STALE DUPLICATE of the profile data in `app-profiles/` (58 slugs, no `adm0`) — without this, Nepal silently vanishes and the sheet renders 58 pins instead of 92. |
|
||||
| `UPG100_DATA_PATH` | **Required.** The generator's default points at a STALE DUPLICATE of the profile data in `plugin-designs/` (58 slugs, no `adm0`) — without this, Nepal silently vanishes and the sheet renders 58 pins instead of 92. |
|
||||
| `OFFLINE=1` | Uses the cached `out/_records.json` instead of calling the API. |
|
||||
| `VITE_API_BASE_URL` | Satisfies the fail-closed config gate. No request is made under `OFFLINE=1`. |
|
||||
| `SERIES_INDEX` | Optional. Overrides the sheet's position in the series. |
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import { fileURLToPath } from 'node:url';
|
|||
import { dirname, join, resolve } from 'node:path';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const DATA = process.env.UPG100_DATA_PATH ? resolve(process.env.UPG100_DATA_PATH) : join(__dirname, '..', 'app-profiles', 'my-upg-100-list', 'data');
|
||||
const DATA = process.env.UPG100_DATA_PATH ? resolve(process.env.UPG100_DATA_PATH) : join(__dirname, '..', 'plugin-designs', 'my-upg-100-list', 'data');
|
||||
const OUT = join(__dirname, 'out');
|
||||
const POSTERS = join(OUT, 'posters');
|
||||
mkdirSync(POSTERS, { recursive: true });
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import { fileURLToPath } from 'node:url';
|
|||
import { dirname, join, resolve } from 'node:path';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const DATA = process.env.UPG100_DATA_PATH ? resolve(process.env.UPG100_DATA_PATH) : join(__dirname, '..', 'app-profiles', 'my-upg-100-list', 'data');
|
||||
const DATA = process.env.UPG100_DATA_PATH ? resolve(process.env.UPG100_DATA_PATH) : join(__dirname, '..', 'plugin-designs', 'my-upg-100-list', 'data');
|
||||
const OUT = join(__dirname, 'out');
|
||||
const POSTERS = join(OUT, 'posters');
|
||||
mkdirSync(POSTERS, { recursive: true });
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ cd doxa-maps/library/scripts/posters
|
|||
cp snapshots/v5-3x3-approved/generate.mjs .
|
||||
cp snapshots/v5-3x3-approved/poster-regions.mjs .
|
||||
# clusters.json only if the DATA also needs rolling back:
|
||||
# cp snapshots/v5-3x3-approved/clusters.json ../../app-profiles/my-upg-100-list/data/
|
||||
# cp snapshots/v5-3x3-approved/clusters.json ../../plugin-designs/my-upg-100-list/data/
|
||||
bun run posters && bun run posters:shoot && bun run posters:previews && bun run build
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -24,14 +24,14 @@ import { fileURLToPath } from 'node:url';
|
|||
import { dirname, join, resolve } from 'node:path';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
// The profile data lives in THIS repo — library/scripts/posters -> app-profiles.
|
||||
// The profile data lives in THIS repo — library/scripts/posters -> plugin-designs.
|
||||
// This generator used to sit outside doxa-maps entirely and reach in via
|
||||
// UPG100_DATA_PATH; forgetting that env var silently rendered a STALE COPY of the
|
||||
// data (that is how Nepal vanished from a poster). Now the default path is the
|
||||
// repo's own profile, so the generator cannot read anyone else's data by accident.
|
||||
const DATA = process.env.UPG100_DATA_PATH
|
||||
? resolve(process.env.UPG100_DATA_PATH)
|
||||
: resolve(__dirname, '..', '..', 'app-profiles', 'my-upg-100-list', 'data');
|
||||
: resolve(__dirname, '..', '..', 'plugin-designs', 'my-upg-100-list', 'data');
|
||||
const OUT = join(__dirname, 'out');
|
||||
const POSTERS = join(OUT, 'posters');
|
||||
mkdirSync(POSTERS, { recursive: true });
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// node build-language-resources.mjs
|
||||
// -> out/language-resources.json (per ISO 639-3 language)
|
||||
// -> ../../app-profiles/gospel-resources/data/resources.json (the app's source)
|
||||
// -> ../../plugin-designs/gospel-resources/data/resources.json (the app's source)
|
||||
//
|
||||
// ═══ WHAT THIS ANSWERS ══════════════════════════════════════════════════════
|
||||
// "Which of the DX people groups have scripture in their own language, and which
|
||||
|
|
@ -46,7 +46,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|||
const OUT = join(__dirname, 'out');
|
||||
const RECORDS = resolve(__dirname, '..', 'posters', 'out', '_records.json');
|
||||
const EBIBLE = join(OUT, 'ebible-translations.csv');
|
||||
const APPDATA = resolve(__dirname, '..', '..', '..', 'app-profiles', 'gospel-resources', 'data');
|
||||
const APPDATA = resolve(__dirname, '..', '..', '..', 'plugin-designs', 'gospel-resources', 'data');
|
||||
|
||||
const POOLS = [{
|
||||
id: 'ebible',
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ const OUT = join(__dirname, 'out');
|
|||
const TABLES = join(OUT, 'tables');
|
||||
// Tables that must NEVER ship publicly (see data-private/README.md).
|
||||
const PRIVATE_TABLES = new Set(['field-access-visas']);
|
||||
const PROFILES = resolve(__dirname, '..', '..', '..', 'app-profiles');
|
||||
const PROFILES = resolve(__dirname, '..', '..', '..', 'plugin-designs');
|
||||
|
||||
const read = (f) => JSON.parse(readFileSync(join(OUT, f), 'utf8'));
|
||||
const has = (f) => existsSync(join(OUT, f));
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ const GATE_CAVEATS = [
|
|||
'A FLOOR IS NOT A FIDELITY PROOF. It catches WHOLESALE coarsening (a 3x point collapse), not a subtle reshape — geometry can be moved, smoothed, or distorted at a constant point count and this passes.',
|
||||
'IT DOES NOT CHECK SHAPE. Points are counted, never compared to a reference outline. A garbled coastline with enough vertices is GREEN.',
|
||||
'gb-CHN-ADM1.geojson HAS NO FLOOR. Shipping 3,264 points vs 2,000 under the same coarsening knobs = 1.63x — too thin to separate honestly. That file is UNGUARDED here; treat it as device-gated.',
|
||||
'THIS REPO HAS 83 FILES NAMED world-adm0.geojson (72 under app/, 10 under public/, 1 under app-profiles/; none in node_modules). A GREEN here covers ONLY the path printed above — the other 82 are GENERATED BUNDLE ARTIFACTS and this floor says NOTHING about their freshness or even their validity. Do NOT infer a second floor is needed: a 2026-08-02 snapshot found the app/ copies were mostly PRE-INDIA BUILDS (161 features / 111,013 points, differing from the guarded file by IND alone — stale, not coarsened, and above this floor). But that snapshot is NOT a durable fact: the copies are rewritten by bundle builds, one was mid-sweep TRUNCATED and would not even parse, and any census of them is a moving target. The durable question is whether bundles are REBUILT AND VALIDATED after a boundary change — a build-pipeline property, not something a point floor can hold. NOTE THE STAKES: measured 2026-08-02, ALL NINE per-app copies under public/doxa-maps/ were missing IND (161 features, built 22:05 the previous day, before India landed). "Stale by one country" IS the India-paints-nothing regression this guard was written for — and the "India paints" assertion above only covers the ONE canonical path, not the generated per-app trees.',
|
||||
'THIS REPO HAS 83 FILES NAMED world-adm0.geojson (72 under app/, 10 under public/, 1 under plugin-designs/; none in node_modules). A GREEN here covers ONLY the path printed above — the other 82 are GENERATED BUNDLE ARTIFACTS and this floor says NOTHING about their freshness or even their validity. Do NOT infer a second floor is needed: a 2026-08-02 snapshot found the app/ copies were mostly PRE-INDIA BUILDS (161 features / 111,013 points, differing from the guarded file by IND alone — stale, not coarsened, and above this floor). But that snapshot is NOT a durable fact: the copies are rewritten by bundle builds, one was mid-sweep TRUNCATED and would not even parse, and any census of them is a moving target. The durable question is whether bundles are REBUILT AND VALIDATED after a boundary change — a build-pipeline property, not something a point floor can hold. NOTE THE STAKES: measured 2026-08-02, ALL NINE per-app copies under public/doxa-maps/ were missing IND (161 features, built 22:05 the previous day, before India landed). "Stale by one country" IS the India-paints-nothing regression this guard was written for — and the "India paints" assertion above only covers the ONE canonical path, not the generated per-app trees.',
|
||||
'THE ADM1 LAZY FIGURE IS MODELED, NOT OBSERVED. resimplify() only ever rewrites world-adm0.geojson, so the ADM1 files have no lazy counterpart that the shipping pipeline actually produces. Their floors were set by applying the same knobs offline.',
|
||||
'STILL DEVICE-GATED: whether the coastlines READ at print size. This converts a wholesale-coarsening regression into a CI failure; it does not replace a human looking at a printed poster.',
|
||||
];
|
||||
|
|
@ -101,7 +101,7 @@ function checkPointFloors() {
|
|||
// coarsens "world-adm0.geojson" to test this floor has 83 candidates and only
|
||||
// ONE is the file read here — and coarsening any other yields a GREEN run that
|
||||
// reads exactly like "the floor does not fire". That happened during this
|
||||
// card's own review (a coarsened app-profiles/.../data/adm0 copy, exit 0, read
|
||||
// card's own review (a coarsened plugin-designs/.../data/adm0 copy, exit 0, read
|
||||
// for a minute as the guard failing). A gate that names an ambiguous file is
|
||||
// under-declaring what it measured, so it says the full path out loud.
|
||||
console.log(` reading from: ${join(ASSETS, 'boundaries')}`);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* Back-compat shim. The real DataSourceManager.js lives in the `api/` seam
|
||||
* folder. New code imports from '@map/api/_registry.js' (or
|
||||
* '@map/api/DataSourceManager.js'). This shim keeps existing
|
||||
* `@map/utils/DataSourceManager.js` imports (3 app-profiles + the barrel)
|
||||
* `@map/utils/DataSourceManager.js` imports (3 plugin-designs + the barrel)
|
||||
* working.
|
||||
*/
|
||||
export { DataSourceManager, dataSourceManager } from '../api/DataSourceManager.js'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# app-profiles/ — where YOUR maps live
|
||||
# plugin-designs/ — where YOUR maps live
|
||||
|
||||
The shared template lives in `../library/` (aliased `@map`) — you never edit it per-map.
|
||||
You build your map by making a folder **here**, `app-profiles/<your-map>/`, and composing the
|
||||
You build your map by making a folder **here**, `plugin-designs/<your-map>/`, and composing the
|
||||
template's building blocks. Your folder is yours (colors, API, i18n); the template is shared
|
||||
(Apache-2.0, propagated to every map on the next build).
|
||||
|
||||
|
|
@ -12,7 +12,7 @@ template's building blocks. Your folder is yours (colors, API, i18n); the templa
|
|||
│ (every map reuses these)
|
||||
│ imported by
|
||||
▼
|
||||
app-profiles/<your-map>/ ── YOURS ──► the map's "main" class (the .vue profile = the Vue
|
||||
plugin-designs/<your-map>/ ── YOURS ──► the map's "main" class (the .vue profile = the Vue
|
||||
│ instance for that map)
|
||||
└── src/ ── YOURS ──► this map's OWN non-reusable components, colors, API, utils
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ template's building blocks. Your folder is yours (colors, API, i18n); the templa
|
|||
|
||||
- **Reusable → `library/`.** The legend, the map, the map-controls are shared, parameterized
|
||||
components every map reuses. Fix once, every map benefits. Never edit it to customize one map.
|
||||
- **This map's own → `app-profiles/<your-map>/`.** It holds two things: the map's **"main" class**
|
||||
- **This map's own → `plugin-designs/<your-map>/`.** It holds two things: the map's **"main" class**
|
||||
(each `.vue` profile *is* the Vue instance for that map) **and** that map's **own non-reusable
|
||||
components** (in `src/`). Nothing here leaks to another map.
|
||||
- **Many maps per bundle.** The bundler bundles one map or many — add another flat `.vue` and rebuild.
|
||||
|
|
@ -42,7 +42,7 @@ encouraged** — create as many as you need:
|
|||
## The pattern in one picture
|
||||
|
||||
```
|
||||
app-profiles/<your-map>/ ← one FOLDER = one BUNDLE, named after the folder
|
||||
plugin-designs/<your-map>/ ← one FOLDER = one BUNDLE, named after the folder
|
||||
├── index.js ← entry: registers the custom element, finds the profiles
|
||||
├── overview.vue ← one .vue = one PROFILE (flat in the folder — no profiles/ subfolder)
|
||||
└── detail.vue ← another profile in the SAME bundle
|
||||
|
|
@ -64,7 +64,7 @@ app-profiles/<your-map>/ ← one FOLDER = one BUNDLE, named after the folder
|
|||
|
||||
```bash
|
||||
# 1. create the folder + one profile
|
||||
mkdir -p app-profiles/<your-map>
|
||||
mkdir -p plugin-designs/<your-map>
|
||||
# index.js ← copy doxa-simple-map/index.js, rename the tag
|
||||
# <name>.vue ← your screen, built from @map/* blocks (flat in the folder)
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ npm run build # → public/doxa-maps/<your-map>/<your-map>.js
|
|||
## Your three things (everything else is shared)
|
||||
|
||||
- **Colors** — how pins are colored. Pick one the template ships (`getColorStrategy('language-family')`),
|
||||
or drop your own in `app-profiles/<your-map>/src/colors/` (start from
|
||||
or drop your own in `plugin-designs/<your-map>/src/colors/` (start from
|
||||
`template-bundle/src/colors/example-mode.js`). Never edit `@map/colors/` to recolor your map.
|
||||
- **API** — your map reads its data from the `profile-config` on the page, so the same bundle points
|
||||
at different data with no rebuild. `DataSourceManager` accepts `csv` (bundled file), `api` (one JSON
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* app-profiles/all-people-groups/index.js
|
||||
* plugin-designs/all-people-groups/index.js
|
||||
*
|
||||
* Bundle entry for the ALL PEOPLE GROUPS map — every people group on earth
|
||||
* (peoplegroups.org, 12,379 group×country rows) around the DOXA set.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* app-profiles/cities-internet/index.js — bundle entry for CITIES WITH INTERNET.
|
||||
* plugin-designs/cities-internet/index.js — bundle entry for CITIES WITH INTERNET.
|
||||
*
|
||||
* Registers ONE custom-element tag:
|
||||
* <cities-internet-map profile-config='{"profile":"connectivity-list"}'></cities-internet-map>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* app-profiles/country-posters/index.js
|
||||
* plugin-designs/country-posters/index.js
|
||||
*
|
||||
* Bundle entry — registers the country-posters web component.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
/**
|
||||
* app-profiles/demo-map/index.js
|
||||
* plugin-designs/demo-map/index.js
|
||||
*
|
||||
* Bundle entry — registers the <demo-map> web component.
|
||||
* Adapted from app-profiles/template-bundle/index.js (the copy-me reference).
|
||||
* Adapted from plugin-designs/template-bundle/index.js (the copy-me reference).
|
||||
*
|
||||
* Folder name = bundle name. This folder builds to exactly one output file:
|
||||
* app/doxa-maps/demo-map/demo-map.js
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@
|
|||
just by using a new value.
|
||||
|
||||
profile application profile name (must match
|
||||
app-profiles/doxa-research-map/<name>.vue)
|
||||
plugin-designs/doxa-research-map/<name>.vue)
|
||||
label header title shown on the gem-frame
|
||||
gem amethyst | sapphire | aquamarine | emerald |
|
||||
topaz | citrine | tourmaline. Omit to auto-cycle.
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@
|
|||
just by using a new value.
|
||||
|
||||
profile application profile name (must match
|
||||
app-profiles/doxa-simple-map/<name>.vue)
|
||||
plugin-designs/doxa-simple-map/<name>.vue)
|
||||
label header title shown on the gem-frame
|
||||
gem amethyst | sapphire | aquamarine | emerald |
|
||||
topaz | citrine | tourmaline. Omit to auto-cycle.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* app-profiles/doxa-simple-map/index.js
|
||||
* plugin-designs/doxa-simple-map/index.js
|
||||
*
|
||||
* Bundle entry — registers the doxa-simple-map web component(s).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ const count = (fn) => languages.value.filter(fn).length
|
|||
|
||||
// ═══ A FIELD THAT IS NOT IN THE TABLE IS NOT A ZERO ═════════════════════════
|
||||
// MEASURED 2026-08-02: the copy of people-group-resources.json served from the
|
||||
// bundle tree predates `has_open_bible_stories`, while the copy under app-profiles/
|
||||
// bundle tree predates `has_open_bible_stories`, while the copy under plugin-designs/
|
||||
// carries it for 38 languages. Counting a missing field gives 0, and a 0 bar reads as
|
||||
// "no language anywhere has this" — a fabricated absence, from a build artifact being
|
||||
// a version behind. So a metric whose FIELD is absent renders as not-measured,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* app-profiles/gospel-resources/index.js — bundle entry for GOSPEL RESOURCES.
|
||||
* plugin-designs/gospel-resources/index.js — bundle entry for GOSPEL RESOURCES.
|
||||
*
|
||||
* Registers ONE custom-element tag:
|
||||
* <gospel-resources-map profile-config='{"profile":"resources-list"}'></gospel-resources-map>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// verify-gospel-click-sheet.mjs — prove the COUNTRY-CLICK SHEET is correct without a GPU.
|
||||
//
|
||||
// node app-profiles/gospel-resources/scripts/verify-gospel-click-sheet.mjs # exits 1 on any failure
|
||||
// node plugin-designs/gospel-resources/scripts/verify-gospel-click-sheet.mjs # exits 1 on any failure
|
||||
//
|
||||
// ═══ WHY THIS EXISTS ════════════════════════════════════════════════════════
|
||||
// Clicking a country on the gospel map needs Mapbox TILES (WebGL), which headless
|
||||
|
|
@ -27,7 +27,7 @@ import { fileURLToPath } from 'node:url';
|
|||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const A = join(__dirname, '..', '..', '..', 'app', 'doxa-maps', 'gospel-resources', 'assets', 'tables');
|
||||
const P = join(__dirname, '..', '..', '..', 'app-profiles', 'gospel-resources', 'data');
|
||||
const P = join(__dirname, '..', '..', '..', 'plugin-designs', 'gospel-resources', 'data');
|
||||
|
||||
const load = (p) => JSON.parse(readFileSync(p, 'utf8'));
|
||||
const countries = (() => { const d = load(join(P, 'by-country.json')).countries; return Array.isArray(d) ? d : Object.values(d); })();
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@
|
|||
* A failure here exits non-zero WITHOUT writing output. Stale honest data beats
|
||||
* fresh data from an instrument nobody proved was working.
|
||||
*
|
||||
* Usage: node app-profiles/gospel-resources/scripts/verify-gospel-links.mjs [--limit N] [--out <path>]
|
||||
* Usage: node plugin-designs/gospel-resources/scripts/verify-gospel-links.mjs [--limit N] [--out <path>]
|
||||
*/
|
||||
import { readFileSync, writeFileSync } from 'node:fs'
|
||||
import { resolve, dirname } from 'node:path'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* app-profiles/key-cities/index.js — bundle entry for KEY CITIES.
|
||||
* plugin-designs/key-cities/index.js — bundle entry for KEY CITIES.
|
||||
*
|
||||
* Registers ONE custom-element tag:
|
||||
* <key-cities-map profile-config='{"profile":"cities-list"}'></key-cities-map>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// adjacency-proof.mjs — slice 09b-2 §1(d), can-fail
|
||||
//
|
||||
// node app-profiles/my-upg-100-list/data/adjacency-proof.mjs
|
||||
// node plugin-designs/my-upg-100-list/data/adjacency-proof.mjs
|
||||
//
|
||||
// Proves:
|
||||
// 1. graph builds; every admin has >=1 neighbour EXCEPT declared island-orphans
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
## Origin (read-only mirror — the RICHER copy)
|
||||
|
||||
```
|
||||
<earlier-bundler-checkout>/app-profiles/my-upg-100-list/data/clusters.json
|
||||
<earlier-bundler-checkout>/plugin-designs/my-upg-100-list/data/clusters.json
|
||||
```
|
||||
|
||||
- **md5:** `3040eadf7d8526de396618b15d7e97f0`
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env node
|
||||
// color-isolation-proof.mjs — slice 09b toolC §1(d) · THE LOAD-BEARING GATE
|
||||
//
|
||||
// node app-profiles/my-upg-100-list/data/color-isolation-proof.mjs
|
||||
// node plugin-designs/my-upg-100-list/data/color-isolation-proof.mjs
|
||||
//
|
||||
// Proves, can-fail, that recoloring a my-upg-100-list zone has ZERO blast radius:
|
||||
//
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env node
|
||||
// generate-region-shades.mjs — slice 09b-9 (color-strategy) §1(b) · the SHADE GENERATOR
|
||||
//
|
||||
// node app-profiles/my-upg-100-list/data/generate-region-shades.mjs # write non-Asia families
|
||||
// node app-profiles/my-upg-100-list/data/generate-region-shades.mjs --dry # print ladder, write nothing
|
||||
// node plugin-designs/my-upg-100-list/data/generate-region-shades.mjs # write non-Asia families
|
||||
// node plugin-designs/my-upg-100-list/data/generate-region-shades.mjs --dry # print ladder, write nothing
|
||||
//
|
||||
// WHAT THIS DOES (qa-color-strategy.md §1b + IDEATION shade sketch):
|
||||
// For each NON-ASIA WAGF region family, give every child zone a DISTINCT SHADE of
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// oceania-color-proof.mjs — slice 09b-oceania-color-fix §1(4) the missed gate
|
||||
//
|
||||
// node app-profiles/my-upg-100-list/data/oceania-color-proof.mjs
|
||||
// node plugin-designs/my-upg-100-list/data/oceania-color-proof.mjs
|
||||
//
|
||||
// THE BUG (09b-oceania-color-fix): oceania-indonesia (union of wagf_region:oceania
|
||||
// + country_code:IDN → 18 ISO3) painted MULTIPLE colors on the map until clicked.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
#!/usr/bin/env node
|
||||
// palette-preview.mjs — slice 09b-9b §1(a–c) · the CYBER/neon palette designer + gate
|
||||
//
|
||||
// node app-profiles/my-upg-100-list/data/palette-preview.mjs # design + assert + (optional) shoot
|
||||
// node app-profiles/my-upg-100-list/data/palette-preview.mjs --assert # math-only (no firefox, no write)
|
||||
// node app-profiles/my-upg-100-list/data/palette-preview.mjs --write # write zone-colors.json then assert
|
||||
// node app-profiles/my-upg-100-list/data/palette-preview.mjs --shoot # write + assert + firefox dark/light shots
|
||||
// node plugin-designs/my-upg-100-list/data/palette-preview.mjs # design + assert + (optional) shoot
|
||||
// node plugin-designs/my-upg-100-list/data/palette-preview.mjs --assert # math-only (no firefox, no write)
|
||||
// node plugin-designs/my-upg-100-list/data/palette-preview.mjs --write # write zone-colors.json then assert
|
||||
// node plugin-designs/my-upg-100-list/data/palette-preview.mjs --shoot # write + assert + firefox dark/light shots
|
||||
//
|
||||
// WHAT THIS DOES (qa-cyber-palette.md §1 a–c):
|
||||
// (a) Designs a curated RGB-gaming-keyboard NEON palette for all 33 zones and
|
||||
|
|
@ -32,7 +32,7 @@ const SHOTS = join(HERE, 'visual-gate-shots');
|
|||
const SETTER = join(HERE, 'set-zone-color.mjs');
|
||||
mkdirSync(SHOTS, { recursive: true });
|
||||
|
||||
const URL = process.env.UPG_URL || 'http://localhost:5180/app-profiles/my-upg-100-list/';
|
||||
const URL = process.env.UPG_URL || 'http://localhost:5180/plugin-designs/my-upg-100-list/';
|
||||
const EL = 'my-upg-100-list-map';
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// pip-proof.mjs — slice 09b-1 §1(d) can-fail proof for the PIP admin-stamp.
|
||||
//
|
||||
// node app-profiles/my-upg-100-list/data/pip-proof.mjs
|
||||
// node plugin-designs/my-upg-100-list/data/pip-proof.mjs
|
||||
//
|
||||
// Asserts (exits 1 on any FAIL — a broken PIP yields 0 stamped -> FAILs):
|
||||
// 1. >=99% of IND+CHN PGs got a REAL admin1 (report stamped/total + fallback).
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// pip-stamp.mjs — slice 09b-1 (Asia repartition GATE)
|
||||
//
|
||||
// node app-profiles/my-upg-100-list/data/pip-stamp.mjs
|
||||
// node plugin-designs/my-upg-100-list/data/pip-stamp.mjs
|
||||
//
|
||||
// Point-in-polygon stamps every PG to its TRUE admin-1.
|
||||
// For each record with finite lat/lng, ray-cast [lng,lat] against every
|
||||
|
|
|
|||
4
plugin-designs/my-upg-100-list/data/proof.sh
vendored
4
plugin-designs/my-upg-100-list/data/proof.sh
vendored
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
# proof.sh — full runnable proof (one command) · updated 09b-6 §1(d) L13
|
||||
#
|
||||
# bash app-profiles/my-upg-100-list/data/proof.sh
|
||||
# bash plugin-designs/my-upg-100-list/data/proof.sh
|
||||
#
|
||||
# Proves, can-fail:
|
||||
# 1. rop1 / full re-cache present (or re-fetch) §1h / 04u §1a
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
# RESULT line is green ONLY when every proof genuinely passed.
|
||||
set -uo pipefail
|
||||
cd "$(dirname "$0")/../../.." # -> repo root
|
||||
PROFILE=app-profiles/my-upg-100-list
|
||||
PROFILE=plugin-designs/my-upg-100-list
|
||||
fail=0
|
||||
|
||||
# run_proof "<label>" <tail-lines> <node-script> [args...]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env node
|
||||
// region-hue-proof.mjs — slice 09b-9 (color-strategy) §1(d) · the CAN-FAIL CT GATE
|
||||
//
|
||||
// node app-profiles/my-upg-100-list/data/region-hue-proof.mjs
|
||||
// node plugin-designs/my-upg-100-list/data/region-hue-proof.mjs
|
||||
//
|
||||
// Proves the tapestry zones inherit their parent WAGF region hue (data-copy only),
|
||||
// scoped to the families recolored THIS pass (Asia is DEFERRED to the Asia agent):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// retag-adm1.mjs — slice 09b-7 §1(a) reproducible ADM1 listId re-tag
|
||||
//
|
||||
// node app-profiles/my-upg-100-list/data/retag-adm1.mjs
|
||||
// node plugin-designs/my-upg-100-list/data/retag-adm1.mjs
|
||||
//
|
||||
// THE BUG (09b-6 honest visual gate): gb-IND-ADM1.geojson + gb-CHN-ADM1.geojson
|
||||
// features carry STALE `listId` tags from the OLD pre-09b-3/09b-4 partition. The
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env node
|
||||
// set-zone-color.mjs — slice 09b toolC §1(c) · the override WRITER
|
||||
//
|
||||
// node app-profiles/my-upg-100-list/data/set-zone-color.mjs <zoneId> <hex>
|
||||
// node app-profiles/my-upg-100-list/data/set-zone-color.mjs <zoneId> --clear
|
||||
// node plugin-designs/my-upg-100-list/data/set-zone-color.mjs <zoneId> <hex>
|
||||
// node plugin-designs/my-upg-100-list/data/set-zone-color.mjs <zoneId> --clear
|
||||
//
|
||||
// Upserts (or clears) one zone color in ./zone-colors.json. This is what toolA
|
||||
// (color picker) + toolB (apply) call. Reproducible: stable formatting, keys
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// source-boundaries.mjs — THE boundary source. One script, every level, one schema.
|
||||
//
|
||||
// node app-profiles/my-upg-100-list/data/source-boundaries.mjs # ADM0 + ADM1
|
||||
// node app-profiles/my-upg-100-list/data/source-boundaries.mjs --level ADM0
|
||||
// node app-profiles/my-upg-100-list/data/source-boundaries.mjs --iso NPL,BGD --force
|
||||
// node app-profiles/my-upg-100-list/data/source-boundaries.mjs --resimplify [tol]
|
||||
// node plugin-designs/my-upg-100-list/data/source-boundaries.mjs # ADM0 + ADM1
|
||||
// node plugin-designs/my-upg-100-list/data/source-boundaries.mjs --level ADM0
|
||||
// node plugin-designs/my-upg-100-list/data/source-boundaries.mjs --iso NPL,BGD --force
|
||||
// node plugin-designs/my-upg-100-list/data/source-boundaries.mjs --resimplify [tol]
|
||||
//
|
||||
// Supersedes source-adm0.mjs (04t) and source-adm0-uncovered.mjs (09b-5), which
|
||||
// fetched the SAME provider through two code paths with two property schemas and
|
||||
|
|
@ -404,8 +404,8 @@ async function buildAdm1({ force }) {
|
|||
if (results.some((r) => r.action === 'written')) {
|
||||
console.log(`\n[adm1] listId was written from the clusters files above. Now re-run the`);
|
||||
console.log(`[adm1] canonical re-tag so the tag has ONE authority and is proven:`);
|
||||
console.log(`[adm1] node app-profiles/my-upg-100-list/data/retag-adm1.mjs`);
|
||||
console.log(`[adm1] node app-profiles/my-upg-100-list/data/zone-resolves-proof.mjs`);
|
||||
console.log(`[adm1] node plugin-designs/my-upg-100-list/data/retag-adm1.mjs`);
|
||||
console.log(`[adm1] node plugin-designs/my-upg-100-list/data/zone-resolves-proof.mjs`);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// visual-gate.mjs — slice 09b-6 §1(g) Playwright-FIREFOX visual gate
|
||||
//
|
||||
// node app-profiles/my-upg-100-list/data/visual-gate.mjs
|
||||
// node plugin-designs/my-upg-100-list/data/visual-gate.mjs
|
||||
//
|
||||
// FIREFOX ONLY (hard rule: always Firefox, never Chrome). NEVER
|
||||
// chromium / mcp-chrome. Loads the live dev bundle, asserts the tapestry paints
|
||||
|
|
@ -31,7 +31,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|||
const SHOTS = join(__dirname, 'visual-gate-shots');
|
||||
mkdirSync(SHOTS, { recursive: true });
|
||||
|
||||
const URL = process.env.UPG_URL || 'http://localhost:5180/app-profiles/my-upg-100-list/';
|
||||
const URL = process.env.UPG_URL || 'http://localhost:5180/plugin-designs/my-upg-100-list/';
|
||||
const EL = 'my-upg-100-list-map';
|
||||
|
||||
let fails = 0;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// zone-resolves-proof.mjs — slice 09b-7 §1(b) the assertion 09b-3/09b-4 MISSED (L17)
|
||||
//
|
||||
// node app-profiles/my-upg-100-list/data/zone-resolves-proof.mjs
|
||||
// node plugin-designs/my-upg-100-list/data/zone-resolves-proof.mjs
|
||||
//
|
||||
// L17: 09b-3/09b-4 rewrote the cluster SELECTORS but never re-tagged the ADM1
|
||||
// geojson `listId`, so 4 zones (odisha, assam, yunnan, tibet) resolved 0 polygons
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* app-profiles/my-upg-100-list/index.js
|
||||
* plugin-designs/my-upg-100-list/index.js
|
||||
*
|
||||
* Bundle entry — registers the my-upg-100-list web component.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Vite multi-entry build target. Emits app/research-dashboard.js.
|
||||
*
|
||||
* Mirrors app-profiles/doxa-research-map/index.js (same ProfileLoader plumbing).
|
||||
* Mirrors plugin-designs/doxa-research-map/index.js (same ProfileLoader plumbing).
|
||||
* Registers a SINGLE tag (UNCHANGED from the original profile — only the bundle folder
|
||||
* is now plural so it can host N dashboard profiles tomorrow):
|
||||
* - <research-dashboard-map> config-driven people-group research dashboard
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* useDashboardLayout.js — reusable drag-resize + persistence engine for the CMV dashboard grid.
|
||||
*
|
||||
* A dashboard shell (see app-profiles/doxa-research-dashboards/research-dashboards.vue) is a CSS
|
||||
* A dashboard shell (see plugin-designs/doxa-research-dashboards/research-dashboards.vue) is a CSS
|
||||
* grid whose three flexible tracks are driven by CSS custom properties:
|
||||
* --facet-w (left facet-rail column width) default 248px
|
||||
* --rail-w (right charts-rail column width) default 312px
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ data) plus a `src/` sandbox laid out for your real code.
|
|||
| Zone | What | Edit it? |
|
||||
|---|---|---|
|
||||
| **`library/`** (imported as `@map/…`) | shared, reusable building blocks — map, legends, toolbars, composables, color + API registries | **No — reuse only.** A change here can break every other map. |
|
||||
| **`app-profiles/<your-map>/src/`** | your private sandbox — screens, colors, API wiring, components, utils | **Yes — build anything.** Nothing here leaks to another map. |
|
||||
| **`plugin-designs/<your-map>/src/`** | your private sandbox — screens, colors, API wiring, components, utils | **Yes — build anything.** Nothing here leaks to another map. |
|
||||
|
||||
Rule of thumb: **local by default.** Code only moves into `library/` once a *second* map genuinely
|
||||
needs it — an expert move, not a first move. To see everything `library/` offers, read
|
||||
|
|
@ -17,14 +17,14 @@ needs it — an expert move, not a first move. To see everything `library/` offe
|
|||
|
||||
## Build a new map — the steps
|
||||
|
||||
1. **Copy this folder** → `app-profiles/<your-map>/`, then change **exactly four things**:
|
||||
1. **Copy this folder** → `plugin-designs/<your-map>/`, then change **exactly four things**:
|
||||
- the **folder name** (= bundle name = output file `<your-map>.js`),
|
||||
- the **tag** in `index.js` (`customElements.define('<your-map>', …)`),
|
||||
- the **tag + `profile` strings** in `index.html` (the staging page must mount your tag),
|
||||
- the **`.vue` filenames** — rename `template-map-a.vue` to your screen's name and delete
|
||||
`template-map-b.vue` if you only want one screen (filename = the `profile` name).
|
||||
Nothing else. Check it: `bun run dev` → your map renders at
|
||||
`http://localhost:5173/app-profiles/<your-map>/index.html`.
|
||||
`http://localhost:5173/plugin-designs/<your-map>/index.html`.
|
||||
2. **List what your map needs** — data, colors, controls (legend, search, share, fullscreen…).
|
||||
3. **Pick pieces from `../../docs/reference.md`** and import them by reference (`@map/…`) — never
|
||||
copy library code into your folder.
|
||||
|
|
@ -72,6 +72,6 @@ dropping another flat `.vue` next to these and rebuilding — no config to edit.
|
|||
|
||||
## See also
|
||||
|
||||
- `src/README.md` — sandbox layout · `../README.md` — the app-profiles authoring guide
|
||||
- `src/README.md` — sandbox layout · `../README.md` — the plugin-designs authoring guide
|
||||
- `../../docs/tutorial.md` — 3 zones + 3 rules in one page · `../../docs/reference.md` — the `@map/…` menu
|
||||
- `../doxa-simple-map/index.js` — a fuller, real-map reference entry
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* app-profiles/template-bundle/index.js
|
||||
* plugin-designs/template-bundle/index.js
|
||||
*
|
||||
* Bundle entry — the minimal, copy-me reference for a MULTI-PROFILE bundle.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
* @bundle template-bundle
|
||||
* @element template-bundle
|
||||
*
|
||||
* This file sits DIRECTLY in the bundle folder (app-profiles/template-bundle/),
|
||||
* This file sits DIRECTLY in the bundle folder (plugin-designs/template-bundle/),
|
||||
* NOT in a nested profiles/ subfolder. Its filename without `.vue` —
|
||||
* `template-map-a` — is the profile name you request from the host page:
|
||||
*
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <all-people-groups> custom element. -->
|
||||
<script src="../all-people-groups.js" integrity="sha384-reLYgfQVOBo4SK15sXYteULZYBjIgAz4ZbSLWISMupJJbuYOeUNix30fEd9NL8sV" crossorigin="anonymous"></script>
|
||||
<script src="../all-people-groups.js" integrity="sha384-j5Ypx+EI9DGlTClzgLs1R8wAnmTnkQ7HgAhHrxyGoVoHDMTlFVY7slU6JNicn77T" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
6
public/doxa-maps/all-people-groups/index.html
generated
6
public/doxa-maps/all-people-groups/index.html
generated
|
|
@ -130,7 +130,7 @@ header.top .sub { margin-top: 3px; font: 12px/1.4 var(--sans); color: var(--mut)
|
|||
|
||||
<script>window.FORGE = {"mode":"built","scope":"bundle","manifestUrl":"../manifest.json","bundle":"all-people-groups","hrefBase":"../"};
|
||||
/* file:// contract: manifest inlined at build — no fetch needed to render. */
|
||||
window.FORGE.manifest = {"schema":"doxa-maps-manifest@1","generatedFrom":"plugin-designs/*/*.vue","embedBase":".","bundleCount":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-reLYgfQVOBo4SK15sXYteULZYBjIgAz4ZbSLWISMupJJbuYOeUNix30fEd9NL8sV","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-reLYgfQVOBo4SK15sXYteULZYBjIgAz4ZbSLWISMupJJbuYOeUNix30fEd9NL8sV\" 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"}]}]};</script>
|
||||
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"}]}]};</script>
|
||||
<script>/**
|
||||
* staging-forge.js — the ONE-PAGE semantic staging environment (root + bundle pages).
|
||||
*
|
||||
|
|
@ -143,7 +143,7 @@ header.top .sub { margin-top: 3px; font: 12px/1.4 var(--sans); color: var(--mut)
|
|||
* - Token UX: resolve ?tk= → localStorage → none. If none, show a token bar
|
||||
* (paste once, stored locally in the browser, never in files/links unless you
|
||||
* use ?tk= on purpose). Maps must Just Work after one paste.
|
||||
* - Auto-generated: whatever app-profiles exist, the tree detects and shows them.
|
||||
* - Auto-generated: whatever plugin-designs exist, the tree detects and shows them.
|
||||
*
|
||||
* file:// contract: classic script, manifest inlined at build (window.FORGE.manifest),
|
||||
* fetch only as dev fallback. No import/export. The DEV-HMR block at the bottom is
|
||||
|
|
@ -229,7 +229,7 @@ function buildFlat() {
|
|||
kind: kind === 'nested' ? 'nested' : 'profile',
|
||||
label: p.profile,
|
||||
sub: p.description || (kind === 'nested' ? 'embeds child web components' : 'one web component = one map'),
|
||||
edit: 'app-profiles/' + b.bundle + '/' + (p.file || p.profile + '.vue'),
|
||||
edit: 'plugin-designs/' + b.bundle + '/' + (p.file || p.profile + '.vue'),
|
||||
href: treeHref(b.bundle + '/' + p.profile + '/index.html'),
|
||||
bundle: b.bundle, element: p.element || (b.elements || [])[0], gem: b.__gem,
|
||||
})
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <cities-internet-map> custom element. -->
|
||||
<script src="../cities-internet.js" integrity="sha384-PAqYfQxuqlTa0wMwATfLtaJIeDnBhlHqijIMW4ILMtpy4VOXubmV19L8iiu3LI/u" crossorigin="anonymous"></script>
|
||||
<script src="../cities-internet.js" integrity="sha384-gPdqkrwfePG3KfeoSRe6X6laMtcVdN7tqsJXm15WUA9UWWhxW9j99P+gyKe/dl8Z" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <cities-internet-map> custom element. -->
|
||||
<script src="../cities-internet.js" integrity="sha384-PAqYfQxuqlTa0wMwATfLtaJIeDnBhlHqijIMW4ILMtpy4VOXubmV19L8iiu3LI/u" crossorigin="anonymous"></script>
|
||||
<script src="../cities-internet.js" integrity="sha384-gPdqkrwfePG3KfeoSRe6X6laMtcVdN7tqsJXm15WUA9UWWhxW9j99P+gyKe/dl8Z" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <cities-internet-map> custom element. -->
|
||||
<script src="../cities-internet.js" integrity="sha384-PAqYfQxuqlTa0wMwATfLtaJIeDnBhlHqijIMW4ILMtpy4VOXubmV19L8iiu3LI/u" crossorigin="anonymous"></script>
|
||||
<script src="../cities-internet.js" integrity="sha384-gPdqkrwfePG3KfeoSRe6X6laMtcVdN7tqsJXm15WUA9UWWhxW9j99P+gyKe/dl8Z" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
6
public/doxa-maps/cities-internet/index.html
generated
6
public/doxa-maps/cities-internet/index.html
generated
|
|
@ -130,7 +130,7 @@ header.top .sub { margin-top: 3px; font: 12px/1.4 var(--sans); color: var(--mut)
|
|||
|
||||
<script>window.FORGE = {"mode":"built","scope":"bundle","manifestUrl":"../manifest.json","bundle":"cities-internet","hrefBase":"../"};
|
||||
/* file:// contract: manifest inlined at build — no fetch needed to render. */
|
||||
window.FORGE.manifest = {"schema":"doxa-maps-manifest@1","generatedFrom":"plugin-designs/*/*.vue","embedBase":".","bundleCount":10,"profileCount":19,"bundles":[{"bundle":"cities-internet","scriptUrl":"./cities-internet/cities-internet.js","output":"doxa-maps/cities-internet/cities-internet.js","integrity":"sha384-PAqYfQxuqlTa0wMwATfLtaJIeDnBhlHqijIMW4ILMtpy4VOXubmV19L8iiu3LI/u","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-PAqYfQxuqlTa0wMwATfLtaJIeDnBhlHqijIMW4ILMtpy4VOXubmV19L8iiu3LI/u\" 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-PAqYfQxuqlTa0wMwATfLtaJIeDnBhlHqijIMW4ILMtpy4VOXubmV19L8iiu3LI/u\" 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-PAqYfQxuqlTa0wMwATfLtaJIeDnBhlHqijIMW4ILMtpy4VOXubmV19L8iiu3LI/u\" crossorigin=\"anonymous\">\u003c/script>\n\u003ccities-internet-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\"}'>\u003c/cities-internet-map>"}}]}]};</script>
|
||||
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>"}}]}]};</script>
|
||||
<script>/**
|
||||
* staging-forge.js — the ONE-PAGE semantic staging environment (root + bundle pages).
|
||||
*
|
||||
|
|
@ -143,7 +143,7 @@ header.top .sub { margin-top: 3px; font: 12px/1.4 var(--sans); color: var(--mut)
|
|||
* - Token UX: resolve ?tk= → localStorage → none. If none, show a token bar
|
||||
* (paste once, stored locally in the browser, never in files/links unless you
|
||||
* use ?tk= on purpose). Maps must Just Work after one paste.
|
||||
* - Auto-generated: whatever app-profiles exist, the tree detects and shows them.
|
||||
* - Auto-generated: whatever plugin-designs exist, the tree detects and shows them.
|
||||
*
|
||||
* file:// contract: classic script, manifest inlined at build (window.FORGE.manifest),
|
||||
* fetch only as dev fallback. No import/export. The DEV-HMR block at the bottom is
|
||||
|
|
@ -229,7 +229,7 @@ function buildFlat() {
|
|||
kind: kind === 'nested' ? 'nested' : 'profile',
|
||||
label: p.profile,
|
||||
sub: p.description || (kind === 'nested' ? 'embeds child web components' : 'one web component = one map'),
|
||||
edit: 'app-profiles/' + b.bundle + '/' + (p.file || p.profile + '.vue'),
|
||||
edit: 'plugin-designs/' + b.bundle + '/' + (p.file || p.profile + '.vue'),
|
||||
href: treeHref(b.bundle + '/' + p.profile + '/index.html'),
|
||||
bundle: b.bundle, element: p.element || (b.elements || [])[0], gem: b.__gem,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <country-posters-map> custom element. -->
|
||||
<script src="../country-posters.js" integrity="sha384-55+Ke4uM9UOSblliojP0Q/PjsXxJ4eeO2Pq8zSIC6m4wPq18T2QCSTyM1NMBz0bo" crossorigin="anonymous"></script>
|
||||
<script src="../country-posters.js" integrity="sha384-X9SslLiOGDh8ASpogPFqNUI34WlQHj7pMHc2hPWMQxnuZfUk67ZFpnBa17eRFLcD" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <country-posters-map> custom element. -->
|
||||
<script src="../country-posters.js" integrity="sha384-55+Ke4uM9UOSblliojP0Q/PjsXxJ4eeO2Pq8zSIC6m4wPq18T2QCSTyM1NMBz0bo" crossorigin="anonymous"></script>
|
||||
<script src="../country-posters.js" integrity="sha384-X9SslLiOGDh8ASpogPFqNUI34WlQHj7pMHc2hPWMQxnuZfUk67ZFpnBa17eRFLcD" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
68
public/doxa-maps/country-posters/country-posters.js
generated
68
public/doxa-maps/country-posters/country-posters.js
generated
File diff suppressed because one or more lines are too long
6
public/doxa-maps/country-posters/index.html
generated
6
public/doxa-maps/country-posters/index.html
generated
|
|
@ -130,7 +130,7 @@ header.top .sub { margin-top: 3px; font: 12px/1.4 var(--sans); color: var(--mut)
|
|||
|
||||
<script>window.FORGE = {"mode":"built","scope":"bundle","manifestUrl":"../manifest.json","bundle":"country-posters","hrefBase":"../"};
|
||||
/* file:// contract: manifest inlined at build — no fetch needed to render. */
|
||||
window.FORGE.manifest = {"schema":"doxa-maps-manifest@1","generatedFrom":"plugin-designs/*/*.vue","embedBase":".","bundleCount":10,"profileCount":19,"bundles":[{"bundle":"country-posters","scriptUrl":"./country-posters/country-posters.js","output":"doxa-maps/country-posters/country-posters.js","integrity":"sha384-55+Ke4uM9UOSblliojP0Q/PjsXxJ4eeO2Pq8zSIC6m4wPq18T2QCSTyM1NMBz0bo","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-55+Ke4uM9UOSblliojP0Q/PjsXxJ4eeO2Pq8zSIC6m4wPq18T2QCSTyM1NMBz0bo\" 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-55+Ke4uM9UOSblliojP0Q/PjsXxJ4eeO2Pq8zSIC6m4wPq18T2QCSTyM1NMBz0bo\" 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>"}}]}]};</script>
|
||||
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>"}}]}]};</script>
|
||||
<script>/**
|
||||
* staging-forge.js — the ONE-PAGE semantic staging environment (root + bundle pages).
|
||||
*
|
||||
|
|
@ -143,7 +143,7 @@ header.top .sub { margin-top: 3px; font: 12px/1.4 var(--sans); color: var(--mut)
|
|||
* - Token UX: resolve ?tk= → localStorage → none. If none, show a token bar
|
||||
* (paste once, stored locally in the browser, never in files/links unless you
|
||||
* use ?tk= on purpose). Maps must Just Work after one paste.
|
||||
* - Auto-generated: whatever app-profiles exist, the tree detects and shows them.
|
||||
* - Auto-generated: whatever plugin-designs exist, the tree detects and shows them.
|
||||
*
|
||||
* file:// contract: classic script, manifest inlined at build (window.FORGE.manifest),
|
||||
* fetch only as dev fallback. No import/export. The DEV-HMR block at the bottom is
|
||||
|
|
@ -229,7 +229,7 @@ function buildFlat() {
|
|||
kind: kind === 'nested' ? 'nested' : 'profile',
|
||||
label: p.profile,
|
||||
sub: p.description || (kind === 'nested' ? 'embeds child web components' : 'one web component = one map'),
|
||||
edit: 'app-profiles/' + b.bundle + '/' + (p.file || p.profile + '.vue'),
|
||||
edit: 'plugin-designs/' + b.bundle + '/' + (p.file || p.profile + '.vue'),
|
||||
href: treeHref(b.bundle + '/' + p.profile + '/index.html'),
|
||||
bundle: b.bundle, element: p.element || (b.elements || [])[0], gem: b.__gem,
|
||||
})
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue