README: three outputs, not five methods; a Languages section; screenshot gates see every shot again
- Deployment is now organised around what the build makes: (1) one web folder (536 files, 76 MiB), embedded by iframe and JavaScript (proven) and WordPress (just for fun); (2) the mobile app (Capacitor, built); (3) the desktop app (Tauri, proof of concept). A hosting table says where the folder can go: dx-life's public/, Cloudflare Pages drag-and-drop (fits its 1,000-file / 25 MiB limits), GitHub + jsDelivr (JS embeds only: measured, jsDelivr serves HTML as text/plain), or any web server, with the one _headers line JS embeds need. No Docker. The diagram and the map-cruncher picture say three outputs. - Languages: 11 locales via ?lang=, Arabic right to left, with an Arabic shot of UPG-100 (Big Picture still leaks English in Arabic; reported). The partner profile-config locale is deliberately not documented until it is fixed. - check-shot-freshness and check-shot-distinctness parsed only '...' paths, so after44ef31b2turned them into `/${TREE_NAME}/...` templates they saw 2 of 26 shots: distinctness failed, freshness passed vacuously and hid 22 stale shots. Both now accept either form and resolve TREE_NAME, and exit 75 (NOT MEASURED) unless every `await shot(` call is parsed (the one dynamic dx-life call is marked). Negative-tested. The 22 shots are retaken and checked by eye. - how-to: the shell no longer overwrites the Studio (it now lives in the output folder); README serve line points at doxa-maps/index.html. - Docs follow the rename to dx-maps (09014f8a): every output-folder path and URL in README, how-to, reference, CONTRIBUTING, the CLI section and the develop guide now says public/dx-maps/. The repo name, the config file name, the WordPress plugin folder and the doxa-* tag names are unchanged (tags are a separate landing). - Hosting: a live example, the dx-life branch research-gallery, which drops the folder into dx-life public/ and fills /research with the Research Gallery.
239
README.md
|
|
@ -8,15 +8,15 @@ Made by Vibe Coders for Vibe Coders to prepare the way of the Lord and make His
|
||||||
|
|
||||||
**Write a map once in `plugin-designs/<your-map>/`, run one build, and drop it on any site** —
|
**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
|
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.
|
capabilities, three outputs, one shared library, one build.
|
||||||
|
|
||||||
| What dx-maps set out to do | Proof |
|
| What dx-maps set out to do | Proof |
|
||||||
|---|---|
|
|---|---|
|
||||||
| **One build → one folder** you can drop on any host | `bun run check:drop` |
|
| **One build → one folder** you can drop on any host | `bun run check:drop` |
|
||||||
| **Every map embeddable** from a copy-paste snippet | [deployment methods](#the-5-deployment-methods) · `bun run check:embed-targets` |
|
| **Every map embeddable** from a copy-paste snippet | [the 3 outputs](#what-the-build-makes--3-outputs) · `bun run check:embed-targets` |
|
||||||
| **Every map interoperable** through props and events | `bun run verify:interop` |
|
| **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](#capability-5--app-shell-the-research-gallery) · `bun run check:composite-reg` |
|
| **One app nests every map** — and each stays embeddable on its own | [capability 5](#capability-5--app-shell-the-research-gallery) · `bun run check:composite-reg` |
|
||||||
| **5 capabilities × 5 deployment methods** | `bun run verify:5-caps` |
|
| **5 capabilities, every one shippable in every output** | `bun run verify:5-caps` |
|
||||||
| **Shared library, private plugins** — reuse without breaking each other | `bun run check:library-index` |
|
| **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](#build-it-with-an-agent-swarm) |
|
| **One agent per plugin, one shepherd, one working `main`** | [agent swarm](#build-it-with-an-agent-swarm) |
|
||||||
|
|
||||||
|
|
@ -88,7 +88,7 @@ and graduates to a named tab when it's ready.
|
||||||
<sub>Also on the shelf: UPG neighbor, all people groups, country posters, the vision card, and the PPLR data maps.</sub>
|
<sub>Also on the shelf: UPG neighbor, all people groups, country posters, the vision card, and the PPLR data maps.</sub>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>How one build becomes five ways to ship — the "map cruncher"</b></summary>
|
<summary><b>How one build becomes three outputs — the "map cruncher"</b></summary>
|
||||||
|
|
||||||
```text
|
```text
|
||||||
map · map · map · map · map · map · map many maps, each once its own codebase
|
map · map · map · map · map · map · map many maps, each once its own codebase
|
||||||
|
|
@ -99,8 +99,12 @@ and graduates to a named tab when it's ready.
|
||||||
|
|
|
|
||||||
ONE build step
|
ONE build step
|
||||||
|
|
|
|
||||||
public/doxa-maps/ one drop-anywhere folder + manifest.json
|
public/dx-maps/ OUTPUT 1: one drop-anywhere web folder
|
||||||
/ | \ \ iframe mobile JS embed WordPress desktop
|
/ | \ how a page shows a map:
|
||||||
|
iframe JS embed WordPress (WordPress just for fun)
|
||||||
|
|
||||||
|
the same folder, wrapped:
|
||||||
|
mobile app (OUTPUT 2) · desktop app (OUTPUT 3)
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
@ -259,43 +263,134 @@ one custom HTML tag, like `<all-people-groups>`, that works on any web page.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## The 5 deployment methods
|
## What the build makes — 3 outputs
|
||||||
|
|
||||||
### 1. iframe — the prayer map on any site ✅ production
|
**One build step makes one folder. That folder is the product** — every other choice is *where you
|
||||||
|
put it* and *how a page shows it*.
|
||||||
|
|
||||||
**Paste one line of HTML and the map appears** — on any website, no coding.
|
| # | Output | What it is | Status |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **1** | **The web folder** `public/dx-maps/` | every map, each embeddable on its own, all working together — 536 files, 76 MiB (about 80 MB) | ✅ production |
|
||||||
|
| **2** | **Mobile app** | the same folder, wrapped as an Android app by Capacitor | ✅ built |
|
||||||
|
| **3** | **Desktop app** | the same folder, wrapped as a desktop app by Tauri | 🧪 proven, not yet in production |
|
||||||
|
|
||||||

|
### Output 1 — the web folder
|
||||||
|
|
||||||
|
Two questions: **where does the folder go** (hosting), and **how does a page show a map** (embedding).
|
||||||
|
|
||||||
|
#### Where the folder goes
|
||||||
|
|
||||||
|
| Put the folder… | How | iframe | JS embed | Good to know |
|
||||||
|
|---|---|:-:|:-:|---|
|
||||||
|
| **in dx-life's `public/` folder** | copy one folder; dx-life becomes the host | ✅ | ✅ | this is how the Research Gallery runs on the dx-life research page (below) |
|
||||||
|
| **on Cloudflare Pages** | drag and drop the folder in the dashboard | ✅ | ✅ | fits the drag-and-drop limits (1,000 files, 25 MiB per file); add the `_headers` file below for JS embeds on other sites |
|
||||||
|
| **on GitHub, served by jsDelivr** | push the repo to a public GitHub repo | — | ✅ | a free CDN for scripts; it serves HTML as plain text, so iframes won't render — use it for JS embeds only |
|
||||||
|
| **on any web server** | copy the folder | ✅ | ✅ | send `Access-Control-Allow-Origin: *` for JS embeds on other sites |
|
||||||
|
|
||||||
|
**Live example:** the dx-life branch [`research-gallery`](https://git.nuxtbase.app/doxa-life/doxa-maps-cdn/src/branch/research-gallery) drops this folder into dx-life's `public/` and fills `/research` with the Research Gallery. Run it with `bun install && bun run dev`, then open `/research/`.
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>More about method 1 — iframe</b></summary>
|
<summary><b>More about hosting the folder</b></summary>
|
||||||
|
|
||||||
An iframe is a window in your page that shows another page — here, the map's own page. It's the
|
- **The folder works from any address.** Every link inside it is relative, so you can put it at
|
||||||
easiest way to share a map: it works on any site that accepts HTML, and the map keeps its own look.
|
`https://your-site/dx-maps/` or at a CDN root and nothing breaks (`bun run check:drop` proves it).
|
||||||
Every plugin's exact snippet is `embed.iframe` in `public/doxa-maps/manifest.json`:
|
- **iframes need nothing special.** A map's page loads inside the frame from wherever the folder is.
|
||||||
|
- **JS embeds on another site need one header.** The embed loads scripts and `manifest.json` across
|
||||||
|
sites with a security hash, so the host must allow it. On Cloudflare Pages (or Netlify), put a file
|
||||||
|
named `_headers` in the folder you upload:
|
||||||
|
|
||||||
```html
|
```text
|
||||||
<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>
|
Access-Control-Allow-Origin: *
|
||||||
```
|
```
|
||||||
|
|
||||||
Host the built `public/doxa-maps/` folder anywhere (it works from any address), then point the
|
- **jsDelivr** serves any public GitHub repo as a CDN, e.g.
|
||||||
iframe at it.
|
`https://cdn.jsdelivr.net/gh/<owner>/<repo>@<tag>/public/dx-maps/<plugin>/<plugin>.js`. It needs a
|
||||||
|
**GitHub** repo (this project's public copy is on its own git host), files under 20 MB (the largest
|
||||||
|
here is 7.5 MB), and it returns HTML as plain text — hence scripts only.
|
||||||
|
- **Netlify Drop** (drag and drop at app.netlify.com/drop) works best under 50 MB; at about 80 MB, use the
|
||||||
|
Netlify CLI instead.
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### 2. Mobile app — UPG-100 on a phone ✅ built
|
#### How a page shows a map
|
||||||
|
|
||||||
**The same maps, wrapped as a phone app.**
|
##### iframe — the prayer map on any site ✅ production
|
||||||
|
|
||||||
<p align="center"><img src="documentation/readme-images/deploy2-mobile-upg100.png" alt="Method 2 — the UPG-100 list at phone size" width="320"></p>
|
**Paste one line of HTML and the map appears** — on any website, no coding.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>More about method 2 — mobile app</b></summary>
|
<summary><b>More about the iframe</b></summary>
|
||||||
|
|
||||||
[Capacitor](https://capacitorjs.com) wraps the built maps into an Android app — the same files,
|
An iframe is a window in your page that shows another page — here, the map's own page. It's the
|
||||||
no new code. Shown here: the UPG-100 prayer lists and posters at phone size, each poster with its
|
easiest way to share a map: it works on any site that accepts HTML, and the map keeps its own look.
|
||||||
own print options.
|
Every plugin's exact snippet is `embed.iframe` in `public/dx-maps/manifest.json`:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<iframe src="https://YOUR_HOST/dx-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>
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
##### JavaScript embed — the Research Gallery on dx-life ✅ production
|
||||||
|
|
||||||
|
**Two lines of code put a whole app inside your own page** — here, the Research Gallery on the
|
||||||
|
dx-life research page.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>More about the JavaScript embed</b></summary>
|
||||||
|
|
||||||
|
Instead of a window onto another page, the map becomes **part of your page**, so your page can talk
|
||||||
|
to it. On dx-life, one embed of the Research Gallery replaces the separate research map, list and
|
||||||
|
regions dashboards. The exact snippet is `embed.script` in `manifest.json`; copy its `integrity`
|
||||||
|
hash from there, because it changes with every build:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script>window.MAP_APP_API_URL="https://pray.doxa.life";</script>
|
||||||
|
<script src="https://YOUR_HOST/dx-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/dx-maps/assets"}'></doxa-maps-spa>
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
##### WordPress / Disciple Tools — 🧪 proven, just for fun
|
||||||
|
|
||||||
|
**We got a JAMstack map running inside WordPress and Disciple Tools.**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>More about WordPress / Disciple Tools</b></summary>
|
||||||
|
|
||||||
|
It works, but it isn't yet a production feature of the map bundler. Two ways to try it:
|
||||||
|
|
||||||
|
- **Easiest:** paste the iframe or JavaScript 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.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### Output 2 — the mobile app ✅ built
|
||||||
|
|
||||||
|
**The same maps, wrapped as a phone app.** No new code — the maps are JAMstack: they fetch their data
|
||||||
|
from a remote API, which is exactly what a mobile app needs.
|
||||||
|
|
||||||
|
<p align="center"><img src="documentation/readme-images/deploy2-mobile-upg100.png" alt="Mobile app — the UPG-100 list at phone size" width="320"></p>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>More about the mobile app</b></summary>
|
||||||
|
|
||||||
|
[Capacitor](https://capacitorjs.com) wraps the built folder into an Android app. Shown here: the
|
||||||
|
UPG-100 prayer lists and posters at phone size, each poster with its own print options.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun run check:mobile # checks the app is wired up correctly — needs no Android tools
|
bun run check:mobile # checks the app is wired up correctly — needs no Android tools
|
||||||
|
|
@ -306,59 +401,16 @@ bun run phone # builds the app and installs it on a phone or emulator
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### 3. JavaScript embed — the Research Gallery on dx-life ✅ production
|
### Output 3 — the desktop app 🧪 proven, not yet in production
|
||||||
|
|
||||||
**Two lines of code put a whole app inside your own page** — here, the Research Gallery on dx-life.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><b>More about method 3 — JavaScript embed</b></summary>
|
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<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>
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
### 4. WordPress / Disciple Tools — 🧪 proven, not yet in production
|
|
||||||
|
|
||||||
**We got a JAMstack map running inside WordPress and Disciple Tools.**
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><b>More about method 4 — WordPress / Disciple Tools</b></summary>
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
### 5. Desktop app (Tauri) — 🧪 proven, not yet in production
|
|
||||||
|
|
||||||
**We got the JAMstack map app running as a desktop app.**
|
**We got the JAMstack map app running as a desktop app.**
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>More about method 5 — desktop app</b></summary>
|
<summary><b>More about the desktop app</b></summary>
|
||||||
|
|
||||||
[Tauri](https://tauri.app) wraps the same built maps into a desktop program: it produced a Linux
|
[Tauri](https://tauri.app) wraps the same built folder 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
|
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.
|
could become a strong desktop app.
|
||||||
|
|
||||||
|
|
@ -373,6 +425,39 @@ bun run tauri:build # builds the app — needs Rust, the Tauri CLI and webvie
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Languages
|
||||||
|
|
||||||
|
**Every map speaks 11 languages** — add `?lang=<code>` to a map's page address. Arabic reads right to left.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<sub>UPG-100 List with `?lang=ar`: the interface is in Arabic; people-group and place names come from the data.</sub>
|
||||||
|
|
||||||
|
| Code | Language | | Code | Language |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `en` | English | | `hi` | हिन्दी — Hindi |
|
||||||
|
| `ar` | العربية — Arabic (right to left) | | `it` | Italiano — Italian |
|
||||||
|
| `de` | Deutsch — German | | `pt` | Português — Portuguese |
|
||||||
|
| `es` | Español — Spanish | | `ro` | Română — Romanian |
|
||||||
|
| `fr` | Français — French | | `ru` | Русский — Russian |
|
||||||
|
| `zh` | 中文 — Chinese | | | |
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>More about languages</b></summary>
|
||||||
|
|
||||||
|
- **Pick a language with the address:** `…/prayer/index.html?lang=fr` shows the map in French. It
|
||||||
|
works on every map's own page and on the Research Gallery. `?locale=` works too.
|
||||||
|
- **Without it,** a map follows the page's own language (`<html lang>`, which WordPress sets), and
|
||||||
|
otherwise shows English.
|
||||||
|
- **Right to left:** in Arabic the whole layout mirrors — the Research Gallery's tab rail moves to
|
||||||
|
the right.
|
||||||
|
- **Where the words live:** `library/i18n/locales/<code>/`, one folder per language. Adding a
|
||||||
|
language: [documentation/architecture/i18n.md](./documentation/architecture/i18n.md).
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Build it with an agent swarm
|
## Build it with an agent swarm
|
||||||
|
|
||||||
**Many AI agents can build many plugins at once without breaking each other's work.** Every plugin
|
**Many AI agents can build many plugins at once without breaking each other's work.** Every plugin
|
||||||
|
|
@ -504,8 +589,8 @@ The studio is a plugin like any other: `plugin-designs/plugin-studio/plugin-stud
|
||||||
|---|---|
|
|---|---|
|
||||||
| `bun run dev` | The Plugin Studio, updating as you save, at http://localhost:5173 |
|
| `bun run dev` | The Plugin Studio, updating as you save, at http://localhost:5173 |
|
||||||
| `BUNDLE=<name> bun x vite build` | Build one plugin only |
|
| `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) |
|
| `BUILD_ALLOW=1 bun run build` | Build everything into `public/dx-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 serve` | Serve the built output folder — the Plugin Studio is http://localhost:4173/dx-maps/index.html |
|
||||||
| `bun run gates:pre` · `bun run gates:post` | The checks that keep `main` working |
|
| `bun run gates:pre` · `bun run gates:post` | The checks that keep `main` working |
|
||||||
| `bun run shots` | Retake these screenshots (`ONLY=<name>` for one) |
|
| `bun run shots` | Retake these screenshots (`ONLY=<name>` for one) |
|
||||||
|
|
||||||
|
|
@ -533,7 +618,7 @@ doxa-maps/
|
||||||
│ ├── colors/ styles/ colour strategies and the house style
|
│ ├── colors/ styles/ colour strategies and the house style
|
||||||
│ ├── i18n/locales/ the 11 languages
|
│ ├── i18n/locales/ the 11 languages
|
||||||
│ └── data/ api/ lookup tables and data sources
|
│ └── data/ api/ lookup tables and data sources
|
||||||
├── public/doxa-maps/ the BUILD OUTPUT — the drop-anywhere folder + manifest.json
|
├── public/dx-maps/ the BUILD OUTPUT — the drop-anywhere folder + manifest.json
|
||||||
├── deployment/ mobile (Capacitor) · WordPress · desktop (Tauri)
|
├── deployment/ mobile (Capacitor) · WordPress · desktop (Tauri)
|
||||||
├── internal/ the build and its checks
|
├── internal/ the build and its checks
|
||||||
└── documentation/ tutorial · how-to · reference · architecture
|
└── documentation/ tutorial · how-to · reference · architecture
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ Full walkthrough + the migration story:
|
||||||
| Command | What | Writes to `public/`? |
|
| Command | What | Writes to `public/`? |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `bun run dev` | HMR plugin gallery at `localhost:5173` — iterate on a map in isolation | **No** |
|
| `bun run dev` | HMR plugin gallery at `localhost:5173` — iterate on a map in isolation | **No** |
|
||||||
| `bun run build` | every bundle → `public/doxa-maps/<bundle>/<bundle>.js` + generated pages | **Yes** |
|
| `bun run build` | every bundle → `public/dx-maps/<bundle>/<bundle>.js` + generated pages | **Yes** |
|
||||||
|
|
||||||
To see a change on the served site: `bun run build` (or `BUNDLE=<name> bun run build:bundle`),
|
To see a change on the served site: `bun run build` (or `BUNDLE=<name> bun run build:bundle`),
|
||||||
then refresh. `dev` never updates the served tree.
|
then refresh. `dev` never updates the served tree.
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ The scripts these commands are grounded in (from this branch's `package.json`):
|
||||||
"phone": "bun run apk:debug && bun run apk:install"
|
"phone": "bun run apk:debug && bun run apk:install"
|
||||||
```
|
```
|
||||||
|
|
||||||
`BUILD_ALLOW=1 bun run build` writes the whole deployable tree to `public/doxa-maps/`, including
|
`BUILD_ALLOW=1 bun run build` writes the whole deployable tree to `public/dx-maps/`, including
|
||||||
`manifest.json`: the machine-readable index that carries a ready-to-paste **iframe** snippet and a
|
`manifest.json`: the machine-readable index that carries a ready-to-paste **iframe** snippet and a
|
||||||
**`<script src>` + SRI** snippet for every plugin.
|
**`<script src>` + SRI** snippet for every plugin.
|
||||||
|
|
||||||
|
|
@ -68,7 +68,7 @@ The scripts these commands are grounded in (from this branch's `package.json`):
|
||||||
|
|
||||||
| # | Deployment method | Install first | Command(s) | Status |
|
| # | Deployment method | Install first | Command(s) | Status |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| 1 | **iframe** | Bun | `bun install` → `BUILD_ALLOW=1 bun run build` → drop `public/doxa-maps/` on any host | ✅ production |
|
| 1 | **iframe** | Bun | `bun install` → `BUILD_ALLOW=1 bun run build` → drop `public/dx-maps/` on any host | ✅ production |
|
||||||
| 2 | **Mobile app (Android)** | Bun for the check; + Android SDK · `adb` · JDK 17 for the APK | `bun run check:mobile` (no toolchain) · `bun run phone` | ✅ built |
|
| 2 | **Mobile app (Android)** | Bun for the check; + Android SDK · `adb` · JDK 17 for the APK | `bun run check:mobile` (no toolchain) · `bun run phone` | ✅ built |
|
||||||
| 3 | **JavaScript embed** | Bun | `BUILD_ALLOW=1 bun run build`, then paste the `embed.script` snippet from `manifest.json` | ✅ production |
|
| 3 | **JavaScript embed** | Bun | `BUILD_ALLOW=1 bun run build`, then paste the `embed.script` snippet from `manifest.json` | ✅ production |
|
||||||
| 4 | **WordPress / Disciple Tools** | Bun (+ `php` for the check) | paste a snippet into a Custom HTML block, or install `deployment/wordpress/doxa-maps-embed/` · `bun run check:wp-embed` | 🧪 proof of concept |
|
| 4 | **WordPress / Disciple Tools** | Bun (+ `php` for the check) | paste a snippet into a Custom HTML block, or install `deployment/wordpress/doxa-maps-embed/` · `bun run check:wp-embed` | 🧪 proof of concept |
|
||||||
|
|
@ -82,7 +82,7 @@ The scripts these commands are grounded in (from this branch's `package.json`):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun install
|
bun install
|
||||||
BUILD_ALLOW=1 bun run build # builds every plugin-designs/* bundle -> public/doxa-maps/
|
BUILD_ALLOW=1 bun run build # builds every plugin-designs/* bundle -> public/dx-maps/
|
||||||
bun run serve # optional: serves public/ at http://localhost:4173
|
bun run serve # optional: serves public/ at http://localhost:4173
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -94,7 +94,7 @@ Each profile in `manifest.json` carries an `embed.iframe` snippet:
|
||||||
style="border:none;border-radius:12px;" allowfullscreen></iframe>
|
style="border:none;border-radius:12px;" allowfullscreen></iframe>
|
||||||
```
|
```
|
||||||
|
|
||||||
**The folder is position-independent.** Snippet URLs are relative to the tree root, so `doxa-maps/`
|
**The folder is position-independent.** Snippet URLs are relative to the tree root, so `dx-maps/`
|
||||||
works dropped on any static host. For absolute URLs, set `embedBaseUrl` in the build config.
|
works dropped on any static host. For absolute URLs, set `embedBaseUrl` in the build config.
|
||||||
|
|
||||||
### 2. Mobile app — Android / Capacitor
|
### 2. Mobile app — Android / Capacitor
|
||||||
|
|
|
||||||
|
|
@ -54,12 +54,12 @@ The build is for publishing, not for looking.
|
||||||
## Step 4 — Build the deployable tree
|
## Step 4 — Build the deployable tree
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
BUILD_ALLOW=1 bun run build # discovers plugin-designs/*, bundles each, writes public/doxa-maps/<plugin>/<plugin>.js
|
BUILD_ALLOW=1 bun run build # discovers plugin-designs/*, bundles each, writes public/dx-maps/<plugin>/<plugin>.js
|
||||||
```
|
```
|
||||||
|
|
||||||
**Exit 0 + every guard PASS = a good build.** Your plugin now exists under `public/doxa-maps/<your-plugin>/`
|
**Exit 0 + every guard PASS = a good build.** Your plugin now exists under `public/dx-maps/<your-plugin>/`
|
||||||
with a standalone leaf page and a `manifest.json` entry. `BUILD_ALLOW=1` is deliberate: a full build first
|
with a standalone leaf page and a `manifest.json` entry. `BUILD_ALLOW=1` is deliberate: a full build first
|
||||||
wipes `public/doxa-maps/`, so it refuses (exit 75) in a shared checkout unless you mean it. To compile only
|
wipes `public/dx-maps/`, so it refuses (exit 75) in a shared checkout unless you mean it. To compile only
|
||||||
your plugin: `BUNDLE=<your-plugin> bun x vite build`. Recipes: [how-to.md](how-to.md).
|
your plugin: `BUNDLE=<your-plugin> bun x vite build`. Recipes: [how-to.md](how-to.md).
|
||||||
|
|
||||||
## Step 5 — Prove it mounts in a real browser
|
## Step 5 — Prove it mounts in a real browser
|
||||||
|
|
@ -79,7 +79,7 @@ build config, as above ([why](#notes-and-history)).
|
||||||
|
|
||||||
## Step 6 — Publish and see it live
|
## Step 6 — Publish and see it live
|
||||||
|
|
||||||
**Copy the built tree to any static host, then embed from it.** The tree is `public/doxa-maps/`; every
|
**Copy the built tree to any static host, then embed from it.** The tree is `public/dx-maps/`; every
|
||||||
plugin's ready-made snippets are in its `manifest.json`.
|
plugin's ready-made snippets are in its `manifest.json`.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|
@ -87,7 +87,7 @@ set -e # first line: a failing check must stop
|
||||||
bun run check:settled # never publish a tree a build is rewriting
|
bun run check:settled # never publish a tree a build is rewriting
|
||||||
bun run check:sri # every leaf's integrity hash matches its bundle
|
bun run check:sri # every leaf's integrity hash matches its bundle
|
||||||
bun run check:embed-targets # every embed snippet points at a real file
|
bun run check:embed-targets # every embed snippet points at a real file
|
||||||
rsync -a --delete public/doxa-maps/ <your-host-checkout>/doxa-maps/
|
rsync -a --delete public/dx-maps/ <your-host-checkout>/dx-maps/
|
||||||
# commit and push the host checkout; for a large tree: git -c http.postBuffer=524288000 push …
|
# commit and push the host checkout; for a large tree: git -c http.postBuffer=524288000 push …
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ element with its own `profile-config`.
|
||||||
<script src="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.min.js"></script>
|
<script src="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.min.js"></script>
|
||||||
|
|
||||||
<!-- The bundle (relative path = host serves it; absolute https = external CDN) -->
|
<!-- The bundle (relative path = host serves it; absolute https = external CDN) -->
|
||||||
<script src="/doxa-maps/my-map/my-map.js"></script>
|
<script src="/dx-maps/my-map/my-map.js"></script>
|
||||||
|
|
||||||
<!-- The map. `profile` = your .vue filename. `tk` = the Mapbox token. -->
|
<!-- The map. `profile` = your .vue filename. `tk` = the Mapbox token. -->
|
||||||
<my-map profile-config='{"profile":"my-map","tk":"pk.eyJ..."}'></my-map>
|
<my-map profile-config='{"profile":"my-map","tk":"pk.eyJ..."}'></my-map>
|
||||||
|
|
@ -48,18 +48,18 @@ You don't have to use the script tag. **Every profile also builds its own standa
|
||||||
a bare, full-screen map at:
|
a bare, full-screen map at:
|
||||||
|
|
||||||
```
|
```
|
||||||
https://<your-cdn>/doxa-maps/<bundle>/<profile>/index.html
|
https://<your-cdn>/dx-maps/<bundle>/<profile>/index.html
|
||||||
```
|
```
|
||||||
|
|
||||||
Link it, share it, or iframe it (perfect for CMSs and tools that only accept iframes, like
|
Link it, share it, or iframe it (perfect for CMSs and tools that only accept iframes, like
|
||||||
Start.me or Notion):
|
Start.me or Notion):
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<iframe src="https://<your-cdn>/doxa-maps/doxa-research-map/research-map/index.html"
|
<iframe src="https://<your-cdn>/dx-maps/doxa-research-map/research-map/index.html"
|
||||||
width="100%" height="600" style="border:none;border-radius:12px;" allowfullscreen></iframe>
|
width="100%" height="600" style="border:none;border-radius:12px;" allowfullscreen></iframe>
|
||||||
```
|
```
|
||||||
|
|
||||||
You never write these snippets by hand — `public/doxa-maps/manifest.json` lists a ready-made
|
You never write these snippets by hand — `public/dx-maps/manifest.json` lists a ready-made
|
||||||
`embed.iframe` and `embed.script` snippet for every profile, and the staging pages show them
|
`embed.iframe` and `embed.script` snippet for every profile, and the staging pages show them
|
||||||
copy-paste ready. Design details: [architecture/embeddability.md](./architecture/embeddability.md).
|
copy-paste ready. Design details: [architecture/embeddability.md](./architecture/embeddability.md).
|
||||||
|
|
||||||
|
|
@ -94,7 +94,7 @@ with `_` or `.` — those are skipped by the build.
|
||||||
Import each with the `@map/…` alias; do not copy their code into your folder.
|
Import each with the `@map/…` alias; do not copy their code into your folder.
|
||||||
6. **Build and verify.**
|
6. **Build and verify.**
|
||||||
```bash
|
```bash
|
||||||
BUNDLE=my-new-map bun run build:bundle # builds just yours → public/doxa-maps/my-new-map/my-new-map.js
|
BUNDLE=my-new-map bun run build:bundle # builds just yours → public/dx-maps/my-new-map/my-new-map.js
|
||||||
bun run verify # data guards (fast, no browser)
|
bun run verify # data guards (fast, no browser)
|
||||||
```
|
```
|
||||||
A green build is not a working map — the build proves the bundle *compiles*; the guards
|
A green build is not a working map — the build proves the bundle *compiles*; the guards
|
||||||
|
|
@ -126,7 +126,7 @@ One parameterized profile can emit **many standalone embed pages** — no extra
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`bun run build` then emits a page **per instance** at `doxa-maps/<bundle>/<id>/index.html` —
|
`bun run build` then emits a page **per instance** at `dx-maps/<bundle>/<id>/index.html` —
|
||||||
each directly linkable/iframe-able with its preset props baked into the page (the base profile
|
each directly linkable/iframe-able with its preset props baked into the page (the base profile
|
||||||
still accepts live props). Instances *split* one profile into many render targets; `tabs`
|
still accepts live props). Instances *split* one profile into many render targets; `tabs`
|
||||||
*nest* many views into one element — different tools.
|
*nest* many views into one element — different tools.
|
||||||
|
|
@ -219,7 +219,7 @@ Pick one home and **every partner site embeds the same map from it** — change
|
||||||
updates everywhere.
|
updates everywhere.
|
||||||
|
|
||||||
**How it's wired** (one file: `vite.doxa-maps-build-config.json`; full field table in
|
**How it's wired** (one file: `vite.doxa-maps-build-config.json`; full field table in
|
||||||
**[reference.md](./reference.md)**): `bun run build` always writes to `public/doxa-maps/<bundle>/`.
|
**[reference.md](./reference.md)**): `bun run build` always writes to `public/dx-maps/<bundle>/`.
|
||||||
Set `paths` to also copy the tree into another host's folder (e.g. doxa-life's `public/`). Set
|
Set `paths` to also copy the tree into another host's folder (e.g. doxa-life's `public/`). Set
|
||||||
`embedBaseUrl` to the public URL of your chosen home — it generates absolute, paste-anywhere embed
|
`embedBaseUrl` to the public URL of your chosen home — it generates absolute, paste-anywhere embed
|
||||||
snippets (leave it empty for relative URLs that work wherever the folder is dropped).
|
snippets (leave it empty for relative URLs that work wherever the folder is dropped).
|
||||||
|
|
@ -240,13 +240,12 @@ Several maps + one layout = a single-page app, served from the same `public/` tr
|
||||||
- `nav.js` — add a sidebar entry (`{ kind, id, label, icon, target }`; `target` controls
|
- `nav.js` — add a sidebar entry (`{ kind, id, label, icon, target }`; `target` controls
|
||||||
desktop/mobile packaging).
|
desktop/mobile packaging).
|
||||||
- the `PROFILES` object in its `index.html` — tell the shell what to mount:
|
- the `PROFILES` object in its `index.html` — tell the shell what to mount:
|
||||||
`{ tag: '<your-element>', src: './doxa-maps/<bundle>/<bundle>.js', views: ['map'] }`.
|
`{ tag: '<your-element>', src: './dx-maps/<bundle>/<bundle>.js', views: ['map'] }`.
|
||||||
3. **Install the shell**: `bun run shell` — copies the wrapper to `public/index.html`, so the
|
3. **Install the shell**: `bun run shell` — copies the wrapper to `public/index.html`, so the
|
||||||
site root IS the SPA and the bundles sit beneath it. (Also required for the mobile app.)
|
site root IS the SPA and the bundles sit beneath it. (Also required for the mobile app.)
|
||||||
> Note: this OVERWRITES the generated root page, which since 2026-09-22 is the **plugin studio**
|
> Note: the build's own front page — the **Plugin Studio** — is `public/dx-maps/index.html`,
|
||||||
> host. That is the intended trade for this opt-in wrapper, and it is not sticky — the next
|
> inside the output folder, so installing the shell at `public/index.html` does not replace it.
|
||||||
> `bun run build` regenerates the studio root. The studio's own leaf stays at
|
> The next `bun run build` leaves the shell's copy alone and regenerates the output folder.
|
||||||
> `public/doxa-maps/plugin-studio/plugin-studio/`, and the archived gallery at `/__gallery`.
|
|
||||||
4. **Build & serve**: `bun run build`, then serve `public/` — the shell lazy-loads each bundle
|
4. **Build & serve**: `bun run build`, then serve `public/` — the shell lazy-loads each bundle
|
||||||
the first time you open its nav entry.
|
the first time you open its nav entry.
|
||||||
|
|
||||||
|
|
@ -257,7 +256,7 @@ Contracts and gotchas (PROFILES shape, tag-vs-bundle names, `--no-token`):
|
||||||
|
|
||||||
## Build the Android / iOS app (optional)
|
## Build the Android / iOS app (optional)
|
||||||
|
|
||||||
**Mobile is separate from `bun run build`.** The default build produces **only** the `doxa-maps`
|
**Mobile is separate from `bun run build`.** The default build produces **only** the `dx-maps`
|
||||||
web folder — the embeddable maps (and the SPA, if configured). It never touches Capacitor. The
|
web folder — the embeddable maps (and the SPA, if configured). It never touches Capacitor. The
|
||||||
Android/iOS app is an **opt-in** step that *reuses* that web build: Capacitor's `webDir` points at
|
Android/iOS app is an **opt-in** step that *reuses* that web build: Capacitor's `webDir` points at
|
||||||
`public/` (`appId: life.doxa.maps`, `appName: "Doxa Maps"`), so `cap sync` wraps the already-built
|
`public/` (`appId: life.doxa.maps`, `appName: "Doxa Maps"`), so `cap sync` wraps the already-built
|
||||||
|
|
@ -296,7 +295,7 @@ The engine is a **runtime parameter** — same bundle, no rebuild. Resolution or
|
||||||
<my-map profile-config='{"profile":"my-map","provider":"maplibre"}'></my-map>
|
<my-map profile-config='{"profile":"my-map","provider":"maplibre"}'></my-map>
|
||||||
|
|
||||||
<!-- or flip any generated page from the URL -->
|
<!-- or flip any generated page from the URL -->
|
||||||
https://<cdn>/doxa-maps/<bundle>/<profile>/index.html?provider=maplibre
|
https://<cdn>/dx-maps/<bundle>/<profile>/index.html?provider=maplibre
|
||||||
```
|
```
|
||||||
|
|
||||||
`maplibre` runs token-free on the OpenFreeMap style (the right GL script/CSS is injected
|
`maplibre` runs token-free on the OpenFreeMap style (the right GL script/CSS is injected
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 820 KiB After Width: | Height: | Size: 816 KiB |
|
Before Width: | Height: | Size: 828 KiB After Width: | Height: | Size: 827 KiB |
|
Before Width: | Height: | Size: 2 MiB After Width: | Height: | Size: 2 MiB |
|
Before Width: | Height: | Size: 2 MiB After Width: | Height: | Size: 2 MiB |
BIN
documentation/readme-images/lang-arabic-upg100.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 425 KiB After Width: | Height: | Size: 317 KiB |
|
Before Width: | Height: | Size: 891 KiB After Width: | Height: | Size: 954 KiB |
|
Before Width: | Height: | Size: 368 KiB After Width: | Height: | Size: 253 KiB |
|
Before Width: | Height: | Size: 1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 751 KiB After Width: | Height: | Size: 733 KiB |
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 205 KiB After Width: | Height: | Size: 205 KiB |
|
Before Width: | Height: | Size: 676 KiB After Width: | Height: | Size: 704 KiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.7 MiB |
|
|
@ -1,35 +1,35 @@
|
||||||
{
|
{
|
||||||
"cap1-single-element": {
|
"cap1-single-element": {
|
||||||
"bundle": "all-people-groups",
|
"bundle": "all-people-groups",
|
||||||
"sha256": "c990929277d9aa5fd0d4ef78362f7b43e328a7c073ac6bf97aadfde5cb942de9"
|
"sha256": "715fa7a854ea0b819f5dbe4ae256d95264984584e6c6933ed321bbb4230b49f7"
|
||||||
},
|
},
|
||||||
"cap2-adoption": {
|
"cap2-adoption": {
|
||||||
"bundle": "doxa-simple-map",
|
"bundle": "doxa-simple-map",
|
||||||
"sha256": "0e11603ed79a26b16456f23d582549830365a2c5095ba9ad7ad363c7d1ba9a90"
|
"sha256": "4f7ecd217d30bcb87a86c3f07f9840b2a500c9637037c4ff64a5e10aba009bf5"
|
||||||
},
|
},
|
||||||
"cap2-engagement": {
|
"cap2-engagement": {
|
||||||
"bundle": "doxa-simple-map",
|
"bundle": "doxa-simple-map",
|
||||||
"sha256": "0e11603ed79a26b16456f23d582549830365a2c5095ba9ad7ad363c7d1ba9a90"
|
"sha256": "4f7ecd217d30bcb87a86c3f07f9840b2a500c9637037c4ff64a5e10aba009bf5"
|
||||||
},
|
},
|
||||||
"cap2-prayer": {
|
"cap2-prayer": {
|
||||||
"bundle": "doxa-simple-map",
|
"bundle": "doxa-simple-map",
|
||||||
"sha256": "0e11603ed79a26b16456f23d582549830365a2c5095ba9ad7ad363c7d1ba9a90"
|
"sha256": "4f7ecd217d30bcb87a86c3f07f9840b2a500c9637037c4ff64a5e10aba009bf5"
|
||||||
},
|
},
|
||||||
"cap3-monolithic-research": {
|
"cap3-monolithic-research": {
|
||||||
"bundle": "doxa-research-map",
|
"bundle": "doxa-research-map",
|
||||||
"sha256": "b849fb71c4a96810d840d9f2dfc208c3e13648429e0ae1a1c5784693e6e5774f"
|
"sha256": "ea9638be007ff6127514e663f1ee79644be5fee4ba7340ef8d3ad6bf18cafc69"
|
||||||
},
|
},
|
||||||
"cap4-upg100-list": {
|
"cap4-upg100-list": {
|
||||||
"bundle": "my-upg-100-list",
|
"bundle": "my-upg-100-list",
|
||||||
"sha256": "fab621d68bdad964da2be67135d0385d40417c6180f008f36cbc0f50076f5e4a"
|
"sha256": "50e6d3caaa49467f9c867e4cc04b5121d965092c96e09a1f3d0a78a7483db480"
|
||||||
},
|
},
|
||||||
"cap4-upg100-mfe": {
|
"cap4-upg100-mfe": {
|
||||||
"bundle": "my-upg-100-list",
|
"bundle": "my-upg-100-list",
|
||||||
"sha256": "fab621d68bdad964da2be67135d0385d40417c6180f008f36cbc0f50076f5e4a"
|
"sha256": "50e6d3caaa49467f9c867e4cc04b5121d965092c96e09a1f3d0a78a7483db480"
|
||||||
},
|
},
|
||||||
"cap4-upg100-poster-wall": {
|
"cap4-upg100-poster-wall": {
|
||||||
"bundle": "my-upg-100-list",
|
"bundle": "my-upg-100-list",
|
||||||
"sha256": "fab621d68bdad964da2be67135d0385d40417c6180f008f36cbc0f50076f5e4a"
|
"sha256": "50e6d3caaa49467f9c867e4cc04b5121d965092c96e09a1f3d0a78a7483db480"
|
||||||
},
|
},
|
||||||
"cap5-doxa-maps-spa": {
|
"cap5-doxa-maps-spa": {
|
||||||
"bundle": "doxa-maps-spa",
|
"bundle": "doxa-maps-spa",
|
||||||
|
|
@ -37,51 +37,55 @@
|
||||||
},
|
},
|
||||||
"cap5-plugin-studio": {
|
"cap5-plugin-studio": {
|
||||||
"bundle": "plugin-studio",
|
"bundle": "plugin-studio",
|
||||||
"sha256": "4d3910b06d55c7fccdba7102be4f9420a0e1368efd1695a6abaf12442b4fcda0"
|
"sha256": "6b8f3ce6cb17075ec26859f12ec0afe0c78bee5dc02b1f9f81b8d699931dc44a"
|
||||||
},
|
},
|
||||||
"deploy2-mobile-upg100": {
|
"deploy2-mobile-upg100": {
|
||||||
"bundle": "my-upg-100-list",
|
"bundle": "my-upg-100-list",
|
||||||
"sha256": "fab621d68bdad964da2be67135d0385d40417c6180f008f36cbc0f50076f5e4a"
|
"sha256": "50e6d3caaa49467f9c867e4cc04b5121d965092c96e09a1f3d0a78a7483db480"
|
||||||
},
|
},
|
||||||
"exp-prayer-walk-planner": {
|
"exp-prayer-walk-planner": {
|
||||||
"bundle": "prayer-walk-planner",
|
"bundle": "prayer-walk-planner",
|
||||||
"sha256": "cee5f2806bd5cc8a7fa4c49db200df643d07cf303fd75ee739e606dd9ee93aee"
|
"sha256": "a06c505f4ae24d03f29657065806f65c4dfc36348a1000178d6cb843aaf455e8"
|
||||||
},
|
},
|
||||||
"exp-wall-of-people-groups": {
|
"exp-wall-of-people-groups": {
|
||||||
"bundle": "wall-of-people-groups",
|
"bundle": "wall-of-people-groups",
|
||||||
"sha256": "ba718e082f77f50d5775ebf43007385b7293f8099e2ca4d79e8ad1753ac4133f"
|
"sha256": "de94f7f53fcfbb757ed8216679e5f563db90cde005ec401104d617fc59c5f105"
|
||||||
|
},
|
||||||
|
"lang-arabic-upg100": {
|
||||||
|
"bundle": "my-upg-100-list",
|
||||||
|
"sha256": "50e6d3caaa49467f9c867e4cc04b5121d965092c96e09a1f3d0a78a7483db480"
|
||||||
},
|
},
|
||||||
"prod-big-picture": {
|
"prod-big-picture": {
|
||||||
"bundle": "dashboard-plus-grid",
|
"bundle": "dashboard-plus-grid",
|
||||||
"sha256": "e90739f622678db4c421c76855a5c39882aabd4ed8dbdd65cbe1385a45c67edc"
|
"sha256": "4ec8da3d155150d391ecbc0ac2f96f60bd2efbb12b3b5e8bdf0a869846dbfa16"
|
||||||
},
|
},
|
||||||
"prod-cities-internet": {
|
"prod-cities-internet": {
|
||||||
"bundle": "cities-internet",
|
"bundle": "cities-internet",
|
||||||
"sha256": "ad145b5a63e7d4b7468a54db53f2f71f397216956fec698d357ae5b841fa8e3b"
|
"sha256": "a62739180efc54c2d9e9216eafe0b290921da6ef83827749ead027945f56dbd7"
|
||||||
},
|
},
|
||||||
"prod-cities-internet-phone": {
|
"prod-cities-internet-phone": {
|
||||||
"bundle": "cities-internet",
|
"bundle": "cities-internet",
|
||||||
"sha256": "ad145b5a63e7d4b7468a54db53f2f71f397216956fec698d357ae5b841fa8e3b"
|
"sha256": "a62739180efc54c2d9e9216eafe0b290921da6ef83827749ead027945f56dbd7"
|
||||||
},
|
},
|
||||||
"prod-gospel-resources": {
|
"prod-gospel-resources": {
|
||||||
"bundle": "gospel-resources",
|
"bundle": "gospel-resources",
|
||||||
"sha256": "943fa394060c009d88a3a8e17651980e9c40077be315fb69c98fed92aa17dce0"
|
"sha256": "8b31fa704a5217be541a804d350551b3f7b7d064eb1096eaecb226ad10e578f4"
|
||||||
},
|
},
|
||||||
"prod-gospel-resources-phone": {
|
"prod-gospel-resources-phone": {
|
||||||
"bundle": "gospel-resources",
|
"bundle": "gospel-resources",
|
||||||
"sha256": "943fa394060c009d88a3a8e17651980e9c40077be315fb69c98fed92aa17dce0"
|
"sha256": "8b31fa704a5217be541a804d350551b3f7b7d064eb1096eaecb226ad10e578f4"
|
||||||
},
|
},
|
||||||
"prod-prayer-phone": {
|
"prod-prayer-phone": {
|
||||||
"bundle": "doxa-simple-map",
|
"bundle": "doxa-simple-map",
|
||||||
"sha256": "0e11603ed79a26b16456f23d582549830365a2c5095ba9ad7ad363c7d1ba9a90"
|
"sha256": "4f7ecd217d30bcb87a86c3f07f9840b2a500c9637037c4ff64a5e10aba009bf5"
|
||||||
},
|
},
|
||||||
"prod-regions": {
|
"prod-regions": {
|
||||||
"bundle": "regions-dashboard",
|
"bundle": "regions-dashboard",
|
||||||
"sha256": "8420509dde0ea1390e0d4ae8d49f80a303d17fa8a38c37755a82b07ab09567f5"
|
"sha256": "3609a2c914d6af247cb6618585d234e9a2c857df83bfe03c2a699c2615d1828d"
|
||||||
},
|
},
|
||||||
"prod-regions-phone": {
|
"prod-regions-phone": {
|
||||||
"bundle": "regions-dashboard",
|
"bundle": "regions-dashboard",
|
||||||
"sha256": "8420509dde0ea1390e0d4ae8d49f80a303d17fa8a38c37755a82b07ab09567f5"
|
"sha256": "3609a2c914d6af247cb6618585d234e9a2c857df83bfe03c2a699c2615d1828d"
|
||||||
},
|
},
|
||||||
"prod-research-gallery-phone": {
|
"prod-research-gallery-phone": {
|
||||||
"bundle": "doxa-maps-spa",
|
"bundle": "doxa-maps-spa",
|
||||||
|
|
@ -89,10 +93,10 @@
|
||||||
},
|
},
|
||||||
"prod-simple-research": {
|
"prod-simple-research": {
|
||||||
"bundle": "research-map-plus-grid",
|
"bundle": "research-map-plus-grid",
|
||||||
"sha256": "9e081907bd697b5ac63531cedbdb34d3f47a09d1d5aee3634722128af1136d0f"
|
"sha256": "8091ad4c3369928fb322ca69fb9319567fc1351a381e1bffbca518a13e78cb53"
|
||||||
},
|
},
|
||||||
"prod-simple-research-phone": {
|
"prod-simple-research-phone": {
|
||||||
"bundle": "research-map-plus-grid",
|
"bundle": "research-map-plus-grid",
|
||||||
"sha256": "9e081907bd697b5ac63531cedbdb34d3f47a09d1d5aee3634722128af1136d0f"
|
"sha256": "8091ad4c3369928fb322ca69fb9319567fc1351a381e1bffbca518a13e78cb53"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -161,35 +161,23 @@
|
||||||
<text x="1032.0" y="1710" font-size="16" fill="#475569">the checks must pass, main stays working</text>
|
<text x="1032.0" y="1710" font-size="16" fill="#475569">the checks must pass, main stays working</text>
|
||||||
<line x1="750" y1="956" x2="750" y2="1786" stroke="#334155" stroke-width="2.5" stroke-dasharray="2 5" opacity=".0"/>
|
<line x1="750" y1="956" x2="750" y2="1786" stroke="#334155" stroke-width="2.5" stroke-dasharray="2 5" opacity=".0"/>
|
||||||
<rect x="60" y="1780" width="1380" height="230" rx="20" fill="#f8fafc" stroke="#cbd5e1" stroke-width="2"/>
|
<rect x="60" y="1780" width="1380" height="230" rx="20" fill="#f8fafc" stroke="#cbd5e1" stroke-width="2"/>
|
||||||
<text x="750" y="1824" text-anchor="middle" font-size="22" font-weight="800" fill="#0f172a">main → ONE build → public/doxa-maps/ → five ways to ship</text>
|
<text x="750" y="1824" text-anchor="middle" font-size="22" font-weight="800" fill="#0f172a">main → ONE build → one web folder → three outputs</text>
|
||||||
<rect x="80.0" y="1852" width="236" height="130" rx="14" fill="#fff" stroke="#15803d" stroke-width="2.5"/>
|
<rect x="80.0" y="1852" width="420" height="130" rx="14" fill="#fff" stroke="#15803d" stroke-width="2.5"/>
|
||||||
<text x="198.0" y="1874" text-anchor="middle" font-size="13" font-weight="800" fill="#64748b">METHOD 1</text>
|
<text x="290.0" y="1874" text-anchor="middle" font-size="13" font-weight="800" fill="#64748b">OUTPUT 1</text>
|
||||||
<text x="198.0" y="1906" text-anchor="middle" font-size="21" font-weight="800" fill="#0f172a">iframe</text>
|
<text x="290.0" y="1906" text-anchor="middle" font-size="22" font-weight="800" fill="#0f172a">The web folder</text>
|
||||||
<text x="198.0" y="1932" text-anchor="middle" font-size="15" fill="#475569">any website</text>
|
<text x="290.0" y="1932" text-anchor="middle" font-size="15" fill="#475569">iframe · JS embed · WordPress (for fun)</text>
|
||||||
<rect x="90.0" y="1946" width="216" height="26" rx="13" fill="#15803d" opacity=".13"/>
|
<rect x="190.0" y="1946" width="200" height="26" rx="13" fill="#15803d" opacity=".13"/>
|
||||||
<text x="198.0" y="1964" text-anchor="middle" font-size="11.5" font-weight="800" fill="#15803d">PRODUCTION</text>
|
<text x="290.0" y="1964" text-anchor="middle" font-size="12" font-weight="800" fill="#15803d">PRODUCTION</text>
|
||||||
<rect x="356.0" y="1852" width="236" height="130" rx="14" fill="#fff" stroke="#15803d" stroke-width="2.5"/>
|
<rect x="540.0" y="1852" width="420" height="130" rx="14" fill="#fff" stroke="#15803d" stroke-width="2.5"/>
|
||||||
<text x="474.0" y="1874" text-anchor="middle" font-size="13" font-weight="800" fill="#64748b">METHOD 2</text>
|
<text x="750.0" y="1874" text-anchor="middle" font-size="13" font-weight="800" fill="#64748b">OUTPUT 2</text>
|
||||||
<text x="474.0" y="1906" text-anchor="middle" font-size="21" font-weight="800" fill="#0f172a">Mobile app</text>
|
<text x="750.0" y="1906" text-anchor="middle" font-size="22" font-weight="800" fill="#0f172a">Mobile app</text>
|
||||||
<text x="474.0" y="1932" text-anchor="middle" font-size="15" fill="#475569">Capacitor</text>
|
<text x="750.0" y="1932" text-anchor="middle" font-size="15" fill="#475569">the same folder, wrapped by Capacitor</text>
|
||||||
<rect x="366.0" y="1946" width="216" height="26" rx="13" fill="#15803d" opacity=".13"/>
|
<rect x="650.0" y="1946" width="200" height="26" rx="13" fill="#15803d" opacity=".13"/>
|
||||||
<text x="474.0" y="1964" text-anchor="middle" font-size="11.5" font-weight="800" fill="#15803d">BUILT</text>
|
<text x="750.0" y="1964" text-anchor="middle" font-size="12" font-weight="800" fill="#15803d">BUILT</text>
|
||||||
<rect x="632.0" y="1852" width="236" height="130" rx="14" fill="#fff" stroke="#15803d" stroke-width="2.5"/>
|
<rect x="1000.0" y="1852" width="420" height="130" rx="14" fill="#fff" stroke="#d97706" stroke-width="2.5"/>
|
||||||
<text x="750.0" y="1874" text-anchor="middle" font-size="13" font-weight="800" fill="#64748b">METHOD 3</text>
|
<text x="1210.0" y="1874" text-anchor="middle" font-size="13" font-weight="800" fill="#64748b">OUTPUT 3</text>
|
||||||
<text x="750.0" y="1906" text-anchor="middle" font-size="21" font-weight="800" fill="#0f172a">JavaScript embed</text>
|
<text x="1210.0" y="1906" text-anchor="middle" font-size="22" font-weight="800" fill="#0f172a">Desktop app</text>
|
||||||
<text x="750.0" y="1932" text-anchor="middle" font-size="15" fill="#475569">script + SRI</text>
|
<text x="1210.0" y="1932" text-anchor="middle" font-size="15" fill="#475569">the same folder, wrapped by Tauri</text>
|
||||||
<rect x="642.0" y="1946" width="216" height="26" rx="13" fill="#15803d" opacity=".13"/>
|
<rect x="1110.0" y="1946" width="200" height="26" rx="13" fill="#d97706" opacity=".13"/>
|
||||||
<text x="750.0" y="1964" text-anchor="middle" font-size="11.5" font-weight="800" fill="#15803d">PRODUCTION</text>
|
<text x="1210.0" y="1964" text-anchor="middle" font-size="12" font-weight="800" fill="#d97706">PROVEN · NOT YET LIVE</text>
|
||||||
<rect x="908.0" y="1852" width="236" height="130" rx="14" fill="#fff" stroke="#d97706" stroke-width="2.5"/>
|
|
||||||
<text x="1026.0" y="1874" text-anchor="middle" font-size="13" font-weight="800" fill="#64748b">METHOD 4</text>
|
|
||||||
<text x="1026.0" y="1906" text-anchor="middle" font-size="21" font-weight="800" fill="#0f172a">WordPress</text>
|
|
||||||
<text x="1026.0" y="1932" text-anchor="middle" font-size="15" fill="#475569">Disciple Tools</text>
|
|
||||||
<rect x="918.0" y="1946" width="216" height="26" rx="13" fill="#d97706" opacity=".13"/>
|
|
||||||
<text x="1026.0" y="1964" text-anchor="middle" font-size="11.5" font-weight="800" fill="#d97706">PROVEN · NOT YET LIVE</text>
|
|
||||||
<rect x="1184.0" y="1852" width="236" height="130" rx="14" fill="#fff" stroke="#d97706" stroke-width="2.5"/>
|
|
||||||
<text x="1302.0" y="1874" text-anchor="middle" font-size="13" font-weight="800" fill="#64748b">METHOD 5</text>
|
|
||||||
<text x="1302.0" y="1906" text-anchor="middle" font-size="21" font-weight="800" fill="#0f172a">Desktop</text>
|
|
||||||
<text x="1302.0" y="1932" text-anchor="middle" font-size="15" fill="#475569">Tauri</text>
|
|
||||||
<rect x="1194.0" y="1946" width="216" height="26" rx="13" fill="#d97706" opacity=".13"/>
|
|
||||||
<text x="1302.0" y="1964" text-anchor="middle" font-size="11.5" font-weight="800" fill="#d97706">PROVEN · NOT YET LIVE</text>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 18 KiB |
|
|
@ -14,7 +14,7 @@ Each bundle is one self-contained `.js` file that registers a custom HTML elemen
|
||||||
| **Fix a legend or control for ALL maps** | edit it once in `library/` (`@map`); rebuild |
|
| **Fix a legend or control for ALL maps** | edit it once in `library/` (`@map`); rebuild |
|
||||||
| **Add a colour strategy** | a new file in `@map/colors/` (shared) or the bundle's `src/colors/` (map-private) |
|
| **Add a colour strategy** | a new file in `@map/colors/` (shared) or the bundle's `src/colors/` (map-private) |
|
||||||
| **See my change while working** | `bun run dev` → http://localhost:5173/ (the Plugin Studio) |
|
| **See my change while working** | `bun run dev` → http://localhost:5173/ (the Plugin Studio) |
|
||||||
| **Build everything** | `BUILD_ALLOW=1 bun run build` → `public/doxa-maps/<bundle>/<bundle>.js` |
|
| **Build everything** | `BUILD_ALLOW=1 bun run build` → `public/dx-maps/<bundle>/<bundle>.js` |
|
||||||
| **Build just one bundle** | `BUNDLE=<name> bun x vite build` |
|
| **Build just one bundle** | `BUNDLE=<name> bun x vite build` |
|
||||||
| **See a change on the served site** | rebuild, then refresh (the dev server never writes `public/`) |
|
| **See a change on the served site** | rebuild, then refresh (the dev server never writes `public/`) |
|
||||||
| **Repoint the API without a rebuild** | set `window.MAP_APP_API_URL` before the bundle loads |
|
| **Repoint the API without a rebuild** | set `window.MAP_APP_API_URL` before the bundle loads |
|
||||||
|
|
@ -86,7 +86,7 @@ doxa-maps/
|
||||||
├── deployment/ ← capacitor/ (mobile) · wordpress/ · src-tauri/ (desktop)
|
├── deployment/ ← capacitor/ (mobile) · wordpress/ · src-tauri/ (desktop)
|
||||||
├── public/ ← THE SERVED WEB-ROOT, committed:
|
├── public/ ← THE SERVED WEB-ROOT, committed:
|
||||||
│ ├── index.html the Plugin Studio host page
|
│ ├── index.html the Plugin Studio host page
|
||||||
│ ├── doxa-maps/<bundle>/ built bundles + manifest.json + leaf pages
|
│ ├── dx-maps/<bundle>/ built bundles + manifest.json + leaf pages
|
||||||
│ └── assets/ posters & data the maps fetch at runtime
|
│ └── assets/ posters & data the maps fetch at runtime
|
||||||
├── vite.config.js ← multi-entry: discovers plugin-designs/<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
|
└── vite.doxa-maps-build-config.json ← output tree name + copy destinations + token settings
|
||||||
|
|
@ -112,7 +112,7 @@ colours or language with **no rebuild**.
|
||||||
// also per page via ?provider= or window.MAP_PROVIDER
|
// also per page via ?provider= or window.MAP_PROVIDER
|
||||||
"assetsBase": "/assets", // where tables/, boundaries/, previews/ are served (see below)
|
"assetsBase": "/assets", // where tables/, boundaries/, previews/ are served (see below)
|
||||||
"shareUrl": "https://…", // optional; the WHOLE url the Share button links/embeds
|
"shareUrl": "https://…", // optional; the WHOLE url the Share button links/embeds
|
||||||
"embedBaseUrl": "https://…/doxa-maps", // optional; tree root the Share button joins embed pages to
|
"embedBaseUrl": "https://…/dx-maps", // optional; tree root the Share button joins embed pages to
|
||||||
"tabs": [ /* parameterized screens read this */ ]
|
"tabs": [ /* parameterized screens read this */ ]
|
||||||
}'></doxa-map>
|
}'></doxa-map>
|
||||||
```
|
```
|
||||||
|
|
@ -139,7 +139,7 @@ i18n, under a **distinct** tag, since `customElements.define()` is one-shot per
|
||||||
| Script | What it does |
|
| Script | What it does |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `bun run dev` | Vite dev server with hot reload at http://localhost:5173. **`/` is the Plugin Studio**; `/__gallery` is the archived bundle index. Serves every bundle at its own html path and `/assets/*` from `public/assets/`. **Writes nothing to `public/`.** |
|
| `bun run dev` | Vite dev server with hot reload at http://localhost:5173. **`/` is the Plugin Studio**; `/__gallery` is the archived bundle index. Serves every bundle at its own html path and `/assets/*` from `public/assets/`. **Writes nothing to `public/`.** |
|
||||||
| `BUILD_ALLOW=1 bun run build` | `clean` → one `vite build` per `plugin-designs/*/` folder → `tree` → static gates. Refuses (exit 75) without `BUILD_ALLOW=1`, because it wipes `public/doxa-maps/` first. Takes a lock; `BUILD_LOCK_WAIT=600` queues instead of failing |
|
| `BUILD_ALLOW=1 bun run build` | `clean` → one `vite build` per `plugin-designs/*/` folder → `tree` → static gates. Refuses (exit 75) without `BUILD_ALLOW=1`, because it wipes `public/dx-maps/` first. Takes a lock; `BUILD_LOCK_WAIT=600` queues instead of failing |
|
||||||
| `BUNDLE=<name> bun x vite build` | builds ONE bundle without cleaning the rest of the tree |
|
| `BUNDLE=<name> bun x vite build` | builds ONE bundle without cleaning the rest of the tree |
|
||||||
| `bun run build:watch` | rebuilds into `public/` on every change |
|
| `bun run build:watch` | rebuilds into `public/` on every change |
|
||||||
| `bun run clean` | wipes and recreates the `public/<name>/` output tree |
|
| `bun run clean` | wipes and recreates the `public/<name>/` output tree |
|
||||||
|
|
@ -153,9 +153,9 @@ i18n, under a **distinct** tag, since `customElements.define()` is one-shot per
|
||||||
by `BUNDLE=<name>`) as a self-contained **IIFE**; the full build loops every folder. Sizes differ with what each
|
by `BUNDLE=<name>`) as a self-contained **IIFE**; the full build loops every folder. Sizes differ with what each
|
||||||
bundle imports (simple ≈ 420 KB, research ≈ 1.3 MB).
|
bundle imports (simple ≈ 420 KB, research ≈ 1.3 MB).
|
||||||
|
|
||||||
**Where output goes.** One bundle → one file: `public/doxa-maps/<bundle>/<bundle>.js`, with CSS injected by
|
**Where output goes.** One bundle → one file: `public/dx-maps/<bundle>/<bundle>.js`, with CSS injected by
|
||||||
JS (no separate `.css`). Beyond it, `tree` writes a standalone, iframe-able `index.html` per bundle, per
|
JS (no separate `.css`). Beyond it, `tree` writes a standalone, iframe-able `index.html` per bundle, per
|
||||||
profile and per preset instance, plus `public/doxa-maps/manifest.json` with ready-made
|
profile and per preset instance, plus `public/dx-maps/manifest.json` with ready-made
|
||||||
`embed.iframe` / `embed.script` snippets for every profile. Full design:
|
`embed.iframe` / `embed.script` snippets for every profile. Full design:
|
||||||
[architecture/embeddability.md](./architecture/embeddability.md). Preset instances:
|
[architecture/embeddability.md](./architecture/embeddability.md). Preset instances:
|
||||||
[how-to.md](./how-to.md#ship-n-preset-variants-of-one-screen-instances).
|
[how-to.md](./how-to.md#ship-n-preset-variants-of-one-screen-instances).
|
||||||
|
|
@ -200,7 +200,7 @@ export MAPBOX_TOKEN=$(python3 -c "import json;print(json.load(open('vite.doxa-ma
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
( node internal/serve-strict.mjs --port 4950 & ) # serve the built tree, loopback only
|
( node internal/serve-strict.mjs --port 4950 & ) # serve the built tree, loopback only
|
||||||
B=http://127.0.0.1:4950/doxa-maps; ARGS=""
|
B=http://127.0.0.1:4950/dx-maps; ARGS=""
|
||||||
for p in demo-map/demo-map cities-internet/connectivity-map doxa-research-map/research-map \
|
for p in demo-map/demo-map cities-internet/connectivity-map doxa-research-map/research-map \
|
||||||
country-posters/countries-map doxa-simple-map/doxa-simple-map \
|
country-posters/countries-map doxa-simple-map/doxa-simple-map \
|
||||||
gospel-resources/resources-map my-upg-100-list/upg-100-list my-upg-100-list/upg-100-mfe; do
|
gospel-resources/resources-map my-upg-100-list/upg-100-list my-upg-100-list/upg-100-mfe; do
|
||||||
|
|
@ -239,7 +239,7 @@ Edit this one file, then rebuild.
|
||||||
|
|
||||||
| Field | What it does |
|
| Field | What it does |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `name` | output folder name (default `doxa-maps`) |
|
| `name` | output folder name (`dx-maps`; a config with no name falls back to `doxa-maps`) |
|
||||||
| `paths` | **extra** folders to copy the whole built tree into — point at a host repo's `public/` and that host serves the maps. Empty ⇒ `public/<name>/` only |
|
| `paths` | **extra** folders to copy the whole built tree into — point at a host repo's `public/` and that host serves the maps. Empty ⇒ `public/<name>/` only |
|
||||||
| `embedBaseUrl` | the public URL the tree is served from, for **absolute, paste-anywhere** snippets. Empty ⇒ relative URLs (position-independent; required for Capacitor) |
|
| `embedBaseUrl` | the public URL the tree is served from, for **absolute, paste-anywhere** snippets. Empty ⇒ relative URLs (position-independent; required for Capacitor) |
|
||||||
| `token` | a `pk.` public Mapbox token baked into every page, so a map works on any host. It is public by design — protected by URL restrictions in the Mapbox dashboard, not by secrecy; `check:public-hygiene` allows `pk.` and fails on `sk.` |
|
| `token` | a `pk.` public Mapbox token baked into every page, so a map works on any host. It is public by design — protected by URL restrictions in the Mapbox dashboard, not by secrecy; `check:public-hygiene` allows `pk.` and fails on `sk.` |
|
||||||
|
|
|
||||||
|
|
@ -50,21 +50,25 @@ if (!existsSync(SRC)) {
|
||||||
const src = readFileSync(SRC, 'utf8')
|
const src = readFileSync(SRC, 'utf8')
|
||||||
|
|
||||||
// Each `await shot('name', '/path', { ...opts })` — capture the opts blob so we can read `vp`.
|
// Each `await shot('name', '/path', { ...opts })` — capture the opts blob so we can read `vp`.
|
||||||
const RE = /shot\(\s*'([^']+)'\s*,\s*'([^']*)'\s*(?:,\s*(\{[\s\S]*?\}))?\s*\)/g
|
const RE = /shot\(\s*'([^']+)'\s*,\s*(?:'([^']*)'|`([^`]*)`)\s*(?:,\s*(\{[\s\S]*?\}))?\s*\)/g
|
||||||
const shots = []
|
const shots = []
|
||||||
for (const m of src.matchAll(RE)) {
|
for (const m of src.matchAll(RE)) {
|
||||||
const [, name, path, opts = ''] = m
|
const [, name, quoted, templated, opts = ''] = m
|
||||||
|
const path = quoted ?? templated // '…' or `/${TREE_NAME}/…` — compared as written, so either form is consistent
|
||||||
const vp = opts.match(/vp:\s*\{\s*width:\s*(\d+)\s*,\s*height:\s*(\d+)/)
|
const vp = opts.match(/vp:\s*\{\s*width:\s*(\d+)\s*,\s*height:\s*(\d+)/)
|
||||||
shots.push({ name, path, vp: vp ? `${vp[1]}x${vp[2]}` : 'default' })
|
shots.push({ name, path, vp: vp ? `${vp[1]}x${vp[2]}` : 'default' })
|
||||||
}
|
}
|
||||||
|
|
||||||
// A matcher that finds almost nothing reads exactly like a clean pass — see check-quoted-scripts.
|
// A matcher that finds almost nothing reads exactly like a clean pass — see check-quoted-scripts.
|
||||||
const MIN_EXPECTED = 8
|
// FLOOR: every `await shot(` call must be parsed. If not, the matcher has drifted from the file (it did
|
||||||
|
// once: a codemod turned the paths into template literals and this gate saw 2 of 26), so the result is
|
||||||
|
// NOT MEASURED (exit 75), never a pass and never a product failure.
|
||||||
|
const CALLS = src.split('\n').filter((l, i, a) => /await shot\(/.test(l) && !/shot-floor: dynamic/.test(a[i - 1] || '')).length
|
||||||
console.log(`SHOT-DISTINCTNESS: ${shots.length} shot(s) declared.`)
|
console.log(`SHOT-DISTINCTNESS: ${shots.length} shot(s) declared.`)
|
||||||
if (shots.length < MIN_EXPECTED) {
|
if (shots.length !== CALLS) {
|
||||||
console.error(`\nSHOT-DISTINCTNESS: FAIL — only ${shots.length} shots parsed (expected >= ${MIN_EXPECTED}).`)
|
console.error(`\nSHOT-DISTINCTNESS: NOT MEASURED — parsed ${shots.length} of ${CALLS} shot( calls in readme-shots.mjs.`)
|
||||||
console.error(' The MATCHER is broken, not the shot list. Fix the regex before trusting this.')
|
console.error(' The MATCHER is out of date with the file, not the shot list. Fix the regex, then re-run.')
|
||||||
process.exit(1)
|
process.exit(75)
|
||||||
}
|
}
|
||||||
|
|
||||||
const bySignature = new Map()
|
const bySignature = new Map()
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,19 @@ function stamp(file) {
|
||||||
|
|
||||||
// Parse `await shot('name', '/path/...')` — the generator is the source of truth for its own map.
|
// Parse `await shot('name', '/path/...')` — the generator is the source of truth for its own map.
|
||||||
const src = readFileSync(SHOTS_SRC, 'utf8')
|
const src = readFileSync(SHOTS_SRC, 'utf8')
|
||||||
const shots = [...src.matchAll(/shot\(\s*'([^']+)'\s*,\s*'([^']*)'/g)].map(m => ({ name: m[1], path: m[2] }))
|
// Paths are written either as '...' or as `/${TREE_NAME}/...` (the tree name comes from the build
|
||||||
|
// config); accept both and resolve the placeholder, or a template-literal shot would be silently skipped.
|
||||||
|
const shots = [...src.matchAll(/shot\(\s*'([^']+)'\s*,\s*(['`])([^'`]*)\2/g)]
|
||||||
|
.map(m => ({ name: m[1], path: m[3].replace(/\$\{TREE_NAME\}/g, TREE_NAME) }))
|
||||||
|
// FLOOR: every `await shot(` call must be parsed, or the gate would silently check fewer shots than
|
||||||
|
// exist (it did: after the TREE_NAME codemod it compared 2 of 26 and passed). Mismatch = NOT MEASURED.
|
||||||
|
{
|
||||||
|
const calls = src.split('\n').filter((l, i, a) => /await shot\(/.test(l) && !/shot-floor: dynamic/.test(a[i - 1] || '')).length
|
||||||
|
if (shots.length !== calls) {
|
||||||
|
console.error(`SHOT-FRESHNESS: NOT MEASURED — parsed ${shots.length} of ${calls} shot( calls in readme-shots.mjs; the matcher is out of date with the file.`)
|
||||||
|
process.exit(75)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const stale = [], missing = [], skipped = []
|
const stale = [], missing = [], skipped = []
|
||||||
let checked = 0
|
let checked = 0
|
||||||
|
|
|
||||||
|
|
@ -390,6 +390,9 @@ await shot('prod-gospel-resources-phone', `/${TREE_NAME}/gospel-resources/resour
|
||||||
await shot('exp-wall-of-people-groups', `/${TREE_NAME}/wall-of-people-groups/wall-of-people-groups/index.html`, { wait: 12000, minElements: 40 })
|
await shot('exp-wall-of-people-groups', `/${TREE_NAME}/wall-of-people-groups/wall-of-people-groups/index.html`, { wait: 12000, minElements: 40 })
|
||||||
await shot('exp-prayer-walk-planner', `/${TREE_NAME}/prayer-walk-planner/prayer-walk-planner/index.html`, { wait: 12000, minElements: 40 })
|
await shot('exp-prayer-walk-planner', `/${TREE_NAME}/prayer-walk-planner/prayer-walk-planner/index.html`, { wait: 12000, minElements: 40 })
|
||||||
|
|
||||||
|
// ---- LANGUAGES: UPG-100 in Arabic (?lang=ar), right-to-left — the most completely translated map ----
|
||||||
|
await shot('lang-arabic-upg100', `/${TREE_NAME}/my-upg-100-list/upg-100-list/index.html?lang=ar`, { wait: 12000, minElements: 200 })
|
||||||
|
|
||||||
// ---- DEPLOY2: mobile — the UPG-100 list as the phone app ----
|
// ---- DEPLOY2: mobile — the UPG-100 list as the phone app ----
|
||||||
// The mobile example is UPG-100 (ruled 2026-09-24; Simple Research was retired as the example because
|
// The mobile example is UPG-100 (ruled 2026-09-24; Simple Research was retired as the example because
|
||||||
// it was never built for phones). Shot once its owner marked the phone layout ready at 88a566f:
|
// it was never built for phones). Shot once its owner marked the phone layout ready at 88a566f:
|
||||||
|
|
@ -403,6 +406,7 @@ await shot('deploy2-mobile-upg100', `/${TREE_NAME}/my-upg-100-list/upg-100-mfe/i
|
||||||
// server is up: DXLIFE_URL=http://127.0.0.1:<port> bun run shots. Without it the shot is skipped
|
// server is up: DXLIFE_URL=http://127.0.0.1:<port> bun run shots. Without it the shot is skipped
|
||||||
// and the committed image stays as it is.
|
// and the committed image stays as it is.
|
||||||
if (process.env.DXLIFE_URL) {
|
if (process.env.DXLIFE_URL) {
|
||||||
|
// shot-floor: dynamic — the URL comes from DXLIFE_URL at run time, so the gates cannot parse it
|
||||||
await shot('deploy3-js-embed-dxlife', process.env.DXLIFE_URL.replace(/\/$/, '') + '/research',
|
await shot('deploy3-js-embed-dxlife', process.env.DXLIFE_URL.replace(/\/$/, '') + '/research',
|
||||||
{ vp: { width: 1440, height: 1100 }, wait: 16000, tokenFree: true, minElements: 300 })
|
{ vp: { width: 1440, height: 1100 }, wait: 16000, tokenFree: true, minElements: 300 })
|
||||||
} else if (!ONLY || ONLY.includes('deploy3-js-embed-dxlife')) {
|
} else if (!ONLY || ONLY.includes('deploy3-js-embed-dxlife')) {
|
||||||
|
|
|
||||||