JAMstack bundler for embeddable map applications
Find a file
doxa-maps-88655c70 df3e383c42 One source for the bridge candidate, and the map shows it too
The table offered 107 groups a next step the map did not show. The candidate is
now computed once, in the pin build beside the curated bridge, and both surfaces
read it — the same rule that put the tier in one place. 1,167 candidates across
the research index; 107 of them reach Doxa groups the curated bridge misses.

The map's sheet renders it as its own weaker panel, and the words carry the
limit: "Possibly a way in — worth checking locally", with the language, how
many groups there speak it and what share of the country they are, and "not
ranked and not recommended". No strength, because none can honestly be
computed. Never for a sign language.

A REGRESSION I INTRODUCED AND ALMOST SHIPPED. The edit that wired this used
pinCandidates without declaring it — a string replace that matched nothing and
said nothing. The ReferenceError died inside the custom-element boundary, so the
map never mounted AND the page reported zero errors. My probe then failed with a
30-second timeout that I had caused separately, by passing playwright's timeout
as the waitForFunction ARG rather than its options, which had been silently
using the default in eleven scripts. Two faults stacked: a broken page that
looked error-free, and a probe too impatient to say so.

Zero page errors is not the same as working, and that is the only reason this
was caught: __grState never appeared, which no assertion about pins could have
explained away. Verified after the fix: 2,107 pins, gap cuts to 370, the
candidate panel renders for Abai Sungai naming Malay, and 25 Deaf sheets still
show no bridge at all.
2026-09-25 09:29:48 +00:00
.github/workflows doxa-maps — portable bundler for embeddable JAMstack maps 2026-09-10 01:53:07 +00:00
data One source for the bridge candidate, and the map shows it too 2026-09-25 09:29:48 +00:00
deployment Commit Cargo.lock: it was ignored by association with real build output 2026-09-22 06:53:27 +00:00
documentation README: retake all-people-groups (new light map) and the research map from their latest builds 2026-09-25 07:24:37 +00:00
internal Plugin studio: host attributes stay on the host; map-load measurement probe 2026-09-25 08:34:52 +00:00
library Merge main into bundler-2/work 2026-09-25 08:26:59 +00:00
plugin-designs One source for the bridge candidate, and the map shows it too 2026-09-25 09:29:48 +00:00
public One source for the bridge candidate, and the map shows it too 2026-09-25 09:29:48 +00:00
serving Research Gallery: embed on someone else's page, in both modes 2026-09-24 19:03:37 +00:00
skills/make-a-doxa-map consolidate docs into documentation/ and deployment shells into deployment/ 2026-09-21 23:23:23 +00:00
.env.example doxa-maps — portable bundler for embeddable JAMstack maps 2026-09-10 01:53:07 +00:00
.gitattributes Rename app-profiles -> plugin-designs; build green under new naming (load-bearing paths updated, guards pass) 2026-09-17 18:18:18 +00:00
.gitignore Move the private field-access dataset out of the repo 2026-09-24 18:34:01 +00:00
bun.lock emit a real desktop binary and .deb for deploy method 5 2026-09-21 21:36:18 +00:00
capacitor.config.json consolidate docs into documentation/ and deployment shells into deployment/ 2026-09-21 23:23:23 +00:00
LICENSE doxa-maps — portable bundler for embeddable JAMstack maps 2026-09-10 01:53:07 +00:00
package.json Merge remote-tracking branch 'origin/main' into rega/research-gallery-embed 2026-09-25 07:22:18 +00:00
README.md README: retake the UPG-100 shots after its map resize (89bd9c5d) 2026-09-25 07:24:36 +00:00
vite.config.js Brand-neutral shared VerticalTabsLayout with lazy nested-element hosting; fix cap-5 prayer tab leaf path; neutralize library identifiers; add Forgejo CI build-gate workflow 2026-09-19 17:24:30 +00:00
vite.doxa-maps-build-config.json Composite pages hand a ?tk= or pasted token to their nested maps, and ask when there is none 2026-09-25 05:35:46 +00:00

Goal

Made by Vibe Coders for Vibe Coders to prepare the way of the Lord and make His paths straight.

Isaiah 40:3 “...Prepare the way for the LORD in the wilderness; make a straight highway for our God in the desert."

dx-maps — one build, maps that embed everywhere

Write a map once in plugin-designs/<your-map>/, run one build, and drop it on any site — iframe, script embed, mobile app — or open every map at once as one app. Five plugin capabilities, five deployment methods, one shared library, one build.

What dx-maps set out to do Proof
One build → one folder you can drop on any host bun run check:drop
Every map embeddable from a copy-paste snippet deployment methods · bun run check:embed-targets
Every map interoperable through props and events bun run verify:interop
One app nests every map — and each stays embeddable on its own capability 5 · bun run check:composite-reg
5 capabilities × 5 deployment methods bun run verify:5-caps
Shared library, private plugins — reuse without breaking each other bun run check:library-index
One agent per plugin, one shepherd, one working main agent swarm

Click any ▸ to read more. Everything below is folded so you can scroll the pictures first.


The 5 plugin capabilities

A capability is how much a plugin nests, from one element (1) to an app of apps (5). Every screenshot is a real plugin from this repo's build.

One plugin that holds every map as a tab — and each tab is still a plugin you can embed on its own.

Capability 5 — the Research Gallery app shell (doxa-maps-spa)

More about capability 5 — the app shell

An app shell is the outer frame of a whole app: a rail of tabs down the side, a header, and navigation. Each tab loads a real plugin from a lower capability, only when you open it, so the app stays fast. Nothing is copied into the shell: when a plugin improves, the shell shows the improvement on the next build.

  • Where it lives: plugin-designs/doxa-maps-spa/ — one element, <doxa-maps-spa>.
  • Changing the tabs: the order and grouping are one list, NAV_MODEL in doxa-maps-spa.vue.
  • New plugins show up by themselves: any plugin that isn't placed in a section lands on the folded Experimental shelf, read from the built manifest.json — no code to edit.
  • A second app shell ships too: the Plugin Studio (below), which holds every plugin for developers.
What the Research Gallery is made of — seven production maps, an Experimental shelf, all five capabilities in one plugin
Research Gallery                          <doxa-maps-spa>          capability 5
├─ Dashboards
│  ├─ Big Picture                         dashboard-plus-grid      capability 4
│  │   ├─ research dashboard              research-dashboard       capability 3
│  │   └─ people-groups grid              people-groups-grid       capability 2
│  ├─ Simple Research                     research-map-plus-grid   capability 4
│  │   ├─ research map                    doxa-research-map        capability 3
│  │   └─ people-groups grid              people-groups-grid       capability 2
│  └─ Regions                             regions-mfe              capability 4
│      ├─ regions list                    regions-list             capability 1
│      └─ region dashboards               region-dashboards        capability 1
├─ Prayer Mobilization
│  ├─ UPG-100 List                        upg-100-mfe              capability 4
│  │   ├─ list                            upg-100-list             capability 1
│  │   └─ poster wall                     poster-wall              capability 1
│  └─ Prayer Progress                     doxa-simple-map          capability 2
├─ Gospel Research
│  ├─ Cities with Internet                connectivity-map         capability 1
│  └─ Gospel Resources                    resources-map            capability 1
└─ Experimental (a folded shelf, filled automatically from manifest.json)
   ├─ prayer walk planner                 prayer-walk-planner      capability 1
   ├─ UPG neighbor                        upg-neighbor             capability 3
   ├─ all people groups                   all-people-groups        capability 1
   ├─ country posters                     country-posters          capability 4
   └─ vision card                         doxa-vision-card         capability 2

Capability 4 — composite: plugins wired together

Several plugins glued into one, talking to each other — and each piece still embeddable alone.

Capability 4 — the UPG-100 composite (my-upg-100-list)

List (capability 1, embeddable alone) Poster wall (capability 1, embeddable alone)
UPG-100 list leaf UPG-100 poster-wall leaf
More about capability 4 — composites

A composite takes plugins that already exist and places them together — side by side, stacked, or as views you switch between — and ships the set as one new element. The pieces talk through props (settings passed in) and events (messages sent out): click a region in one piece and another piece updates. Nothing is copied, so each piece can still be embedded on its own.

  • Example: plugin-designs/my-upg-100-list/ — the UPG-100 list and its poster wall, joined into upg-100-mfe. Big Picture, Simple Research and Regions are composites too.
  • How to declare one: a <name>.meta.json beside it with "type": "nested-elements-mfe" and a "composes" list of the plugins inside.
  • The layouts that do the placing: VerticalTabsLayout and StackLayout in library/layouts/.

Capability 3 — multi-view: many screens, one element

One plugin with several screens inside — tabs you switch between.

Capability 3 — the monolithic research map (doxa-research-map)

More about capability 3 — multi-view

All the screens live in one plugin, so they share code and data and switch instantly. The trade-off: you embed the whole thing — you can't embed just one of its tabs. When you need that, split the tabs into separate plugins and join them with a composite (capability 4).

  • Example: plugin-designs/doxa-research-map/ — Prayer, Engagement, People Groups, Regions, Languages and Religions in one map.
  • How to declare one: a <name>.meta.json with "type": "monolithic".

Capability 2 — presets: one plugin, many maps

The same map code, several ready-made versions — each with its own page and embed code.

Prayer Engagement Adoption
Capability 2 — prayer preset (doxa-simple-map) Capability 2 — engagement preset (doxa-simple-map) Capability 2 — adoption preset (doxa-simple-map)
More about capability 2 — presets

You write one map, then list its versions in a small table: a name and the settings for each. The build turns every row into its own standalone page and embed snippet — same code, different map. Adding a version means adding a row, not writing code.

  • Example: plugin-designs/doxa-simple-map/ — Prayer, Engagement and Adoption from one map.
  • How to make one: add <name>.instances.json beside the plugin: { "instances": [{ "id": "prayer", "title": "Prayer", "props": { … } }] }.

Capability 1 — single element

One folder, one map, one tag you can paste into any page.

Capability 1 — a single-element plugin (all-people-groups)

More about capability 1 — single element

The simplest plugin, and where every map starts: a folder with three files — the map's .vue, an index.js that registers its tag, and an index.html for previewing it. The build turns it into one custom HTML tag, like <all-people-groups>, that works on any web page.

  • Example: plugin-designs/all-people-groups/ — every people group on one map.
  • How to make one: copy plugin-designs/template-bundle/ to plugin-designs/<your-map>/ and rename it. The studio finds it automatically.
  • Which plugin is the example of each capability is generated, not remembered: documentation/reference-capability-exemplars.md.

The 5 deployment methods

1. iframe — the prayer map on any site ✅ production

Paste one line of HTML and the map appears — on any website, no coding.

Method 1 — the prayer map embedded in a host page's iframe

More about method 1 — iframe

An iframe is a window in your page that shows another page — here, the map's own page. It's the easiest way to share a map: it works on any site that accepts HTML, and the map keeps its own look. Every plugin's exact snippet is embed.iframe in public/doxa-maps/manifest.json:

<iframe src="https://YOUR_HOST/doxa-maps/doxa-simple-map/doxa-simple-map/prayer/index.html?utm_source=YOUR_SOURCE"
        width="100%" height="600" style="border:none;border-radius:12px;" allowfullscreen></iframe>

Host the built public/doxa-maps/ folder anywhere (it works from any address), then point the iframe at it.

2. Mobile app — UPG-100 on a phone ✅ built

The same maps, wrapped as a phone app.

Method 2 — the UPG-100 list at phone size

More about method 2 — mobile app

Capacitor wraps the built maps into an Android app — the same files, no new code. Shown here: the UPG-100 prayer lists and posters at phone size, each poster with its own print options.

bun run check:mobile   # checks the app is wired up correctly — needs no Android tools
bun run phone          # builds the app and installs it on a phone or emulator

bun run phone needs the Android SDK, JDK 17 and adb. Only Android is set up; iOS is not yet.

Two lines of code put a whole app inside your own page — here, the Research Gallery on dx-life.

Method 3 — the Research Gallery JS-embedded on the dx-life research page

More about method 3 — JavaScript embed

Instead of a window onto another page, the map becomes part of your page, so your page can talk to it. The dx-life research page replaced its separate research map, list and regions dashboards with this one embed of the Research Gallery. The exact snippet is embed.script in manifest.json; copy its integrity hash from there, because it changes with every build:

<script>window.MAP_APP_API_URL="https://pray.doxa.life";</script>
<script src="https://YOUR_HOST/doxa-maps/doxa-maps-spa/doxa-maps-spa.js"
        integrity="sha384-…" crossorigin="anonymous"></script>
<doxa-maps-spa profile-config='{"profile":"doxa-maps-spa","tk":"YOUR_MAPBOX_TOKEN","utmSource":"YOUR_SOURCE","assetsBase":"https://YOUR_HOST/doxa-maps/assets"}'></doxa-maps-spa>

4. WordPress / Disciple Tools — 🧪 proven, not yet in production

We got a JAMstack map running inside WordPress and Disciple Tools.

Method 4 — WordPress / Disciple Tools, proven but not yet in production

More about method 4 — WordPress / Disciple Tools

It works, but it isn't yet a production feature of the map bundler. Two ways to try it:

  • Easiest: paste a method 1 or method 3 snippet into a WordPress Custom HTML block.
  • As a plugin: install deployment/wordpress/doxa-maps-embed/ in wp-content/plugins/. It adds a [doxa_map] shortcode and a block for the block editor.

bun run check:wp-embed confirms the plugin writes the right HTML, without needing WordPress installed.

5. Desktop app (Tauri) — 🧪 proven, not yet in production

We got the JAMstack map app running as a desktop app.

Method 5 — desktop app (Tauri), proven but not yet in production

More about method 5 — desktop app

Tauri wraps the same built maps into a desktop program: it produced a Linux app and a .deb installer. It isn't in the dx bundler for production yet — and the Research Gallery could become a strong desktop app.

bun run check:tauri    # checks the desktop setup — needs no Rust tools
bun run tauri:build    # builds the app — needs Rust, the Tauri CLI and webview libraries

WordPress and Tauri marks from Simple Icons (CC0); the trademarks belong to their owners.


Build it with an agent swarm

Many AI agents can build many plugins at once without breaking each other's work. Every plugin has one agent building it, and one shepherd agent makes sure every agent does its job well. All the plugins live in one project with one shared library, and changes are added to main — the one official version — one at a time.

The delegation model: main at the centre guarded by a merge queue, inside the Plugin Studio; eight plugins around it; one agent per plugin; and one shepherd over all the agents

What problem does this solve? (in plain words)

All the maps here share one project, one library and one build step. That's what makes them work together, and it's also the danger: if one agent's change breaks the shared library, every map breaks at once. And when several agents work in the same project at the same time, they step on each other — two agents edit the same file, one change quietly erases another, or half-finished work lands in the version everyone else is using.

So the project works like a busy kitchen with one head chef:

  1. Each agent works in its own copy of the project — its own counter, so nobody bumps anybody.
  2. Before adding its work, each agent catches up: it pulls in everyone else's latest work and fixes any clashes with its own. The agent fixes the clash, not the next person.
  3. Only one agent adds work at a time. They wait in a line (the merge queue), the checks run, and only then does the change go into main. main always works.
  4. A shepherd agent watches everyone: plugins built in the studio, shared parts really shared, and every change going through the line.

That's how many plugins get built at the same time, in one project, without the maps breaking.

The words, if you're new to git
Word In plain terms
repo the project folder, with its full history
main the one official version of the project that everyone builds from
worktree a private copy of the project for one agent to work in
merge conflict two changes touched the same lines; someone has to decide how to combine them
rebase catching your work up with everyone else's latest before adding it
merge queue the line agents wait in, so only one change goes into main at a time
The commands (for the curious)
# one private copy (worktree) + one agent per plugin
git worktree add -b regions/work ../dx-maps-regions main
git worktree add -b upg100/work  ../dx-maps-upg100  main

# one preview server per agent, each on its own port
bun x vite --port 5301 --strictPort

# add work through the line: one at a time, caught up first, checks must pass
flock /tmp/dx-maps-main.lock sh -c '
  git fetch origin && git rebase origin/main &&
  bun run gates:pre && git push origin HEAD:main'

One agent owns the full build (it rebuilds everyone's output); the others check just their own plugin with BUNDLE=<name> bun x vite build. How to contribute, and the bar for shared code: documentation/CONTRIBUTING.md. Diagram source: delegation-model.svg.


The Plugin Studio — ★ where you work

Every plugin in one place, sorted by capability. Pick any plugin to run it live, fold open the plugins inside it, and copy its embed code. Tell your agent which plugins to group, and it builds a higher-capability plugin from them — that's how the Research Gallery was built.

Capability 5 — the developer studio (plugin-studio)

Get it running

You need git, Bun and Node ≥ 18, plus a free Mapbox public token (it starts with pk.) so the maps have a background.

git clone https://git.nuxtbase.app/doxa-open/doxa-maps.git
cd doxa-maps
bun install --frozen-lockfile
cp .env.example .env            # then paste your pk. token after VITE_MAPBOX_TOKEN=
bun run dev                     # → http://localhost:5173/  is the Plugin Studio

No build needed first — the built maps are already in the repo. To start a plugin, copy plugin-designs/template-bundle/ to plugin-designs/<your-map>/; the studio finds it automatically.

Make changes to the studio itself

The studio is a plugin like any other: plugin-designs/plugin-studio/plugin-studio.vue.

  1. Run bun run dev and open http://localhost:5173/ — that page is the studio.

  2. Edit plugin-studio.vue (or ask your agent to). The page updates as you save.

  3. When it looks right, build just the studio and refresh its security hashes:

    BUNDLE=plugin-studio bun x vite build
    bun run fix:sri
    
  4. Check it opens every plugin: bun run smoke:studio (with bun run dev running).

No Mapbox token? And other map settings
  • No token: set "provider": "maplibre" on any plugin (or add ?provider=maplibre to a map's page) and it draws with MapLibre, which needs no key.
  • Built pages take a token from ?tk=pk.… in the address, from a token you paste once into the page, or from a token endpoint.
  • Every command and prerequisite, per deployment method: documentation/README-SECTION-3-cli-commands.md.
Command What it does
bun run dev The Plugin Studio, updating as you save, at http://localhost:5173
BUNDLE=<name> bun x vite build Build one plugin only
BUILD_ALLOW=1 bun run build Build everything into public/doxa-maps/ (~4 min; refuses without the flag, to protect a shared project)
bun run serve Serve the built maps at http://localhost:4173
bun run gates:pre · bun run gates:post The checks that keep main working
bun run shots Retake these screenshots (ONLY=<name> for one)

Repository layout

plugin-designs/   YOUR maps — one folder per plugin, a free zone
library/          the SHARED toolkit (layouts, components, composables) — a high bar
public/doxa-maps/ the build output: the drop-anywhere folder + manifest.json
deployment/       mobile (Capacitor) · WordPress · desktop (Tauri)
internal/         the build and its checks
documentation/    tutorial · how-to · reference · architecture — start at documentation/README.md

Maps should be open source, run everywhere, and multiply. One build makes each map a self-contained file for your site, your partners' sites and a phone. The Plugin Studio is where you compose them into something bigger.