This commit is contained in:
commit
de7388cbf7
1 changed files with 85 additions and 156 deletions
|
|
@ -60,34 +60,33 @@ The scripts these commands are grounded in (from this branch's `package.json`):
|
|||
"phone": "bun run apk:debug && bun run apk:install"
|
||||
```
|
||||
|
||||
`bun run build` writes the whole deployable tree to `public/doxa-maps/`, including
|
||||
`manifest.json` — the machine-readable index that carries the ready-to-paste
|
||||
**iframe** and **`<script src>` + SRI** embed snippets for every map.
|
||||
`BUILD_ALLOW=1 bun run build` writes the whole deployable tree to `public/doxa-maps/`, including
|
||||
`manifest.json`: the machine-readable index that carries a ready-to-paste **iframe** snippet and a
|
||||
**`<script src>` + SRI** snippet for every plugin.
|
||||
|
||||
### Summary table
|
||||
|
||||
| # | Deploy method | Install first (prereqs) | Command(s) |
|
||||
|---|---------------|-------------------------|------------|
|
||||
| 1 | **iframe embed** | Bun | `bun install` → `bun run build` → `bun run serve` (or drop `public/doxa-maps/` on any host) |
|
||||
| 2 | **Mobile app (Android)** | Bun ALONE for `check:mobile`; + Android SDK · `adb` on PATH · JDK 17 (Gradle) for the APK | `bun run check:mobile` (sync + webDir assertion, NO toolchain needed) · `bun run phone` (= `apk:debug` + `apk:install` to a device/emulator) |
|
||||
| 3 | **Direct-JS / dashboard** | Bun | `bun run build`, then paste the `<script src> + integrity` (SRI) snippet from `manifest.json`. *Proven same-root AND foreign-host (verified 2026-09-20: gl + attribution, 0 errors, 0 404s). Fill the `YOUR_*` placeholders the snippet ships with* |
|
||||
| 4 | **WordPress** | Bun (+ `php` CLI to run the contract gate) | `bun run build`, then either paste the iframe/script snippet into a **Custom HTML** block, **or** install `deployment/wordpress/doxa-maps-embed/` (shortcode + Gutenberg block). `bun run check:wp-embed` verifies the emitted markup. *(Contract-proven; not yet shot on a live WP install.)* |
|
||||
| 5 | **Desktop (Tauri)** | Bun ALONE for `check:tauri`; + Rust toolchain · Tauri CLI · system webview libs for the binary | `bun run check:tauri` (config contract, NO toolchain needed) · `cargo tauri build` (needs the toolchain). *(Built, not shot — a 65 MB binary + 58 MB `.deb` WERE emitted; only the GUI launch is unverified.)* |
|
||||
| # | 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 5 | **Desktop (Tauri)** | Bun for the check; + Rust · Tauri CLI · webview libs for the binary | `bun run check:tauri` (no toolchain) · `bun run tauri:build` | 🧪 proof of concept |
|
||||
|
||||
---
|
||||
|
||||
### 1. iframe embed *(required)*
|
||||
### 1. iframe
|
||||
|
||||
**Install:** Bun.
|
||||
|
||||
```bash
|
||||
bun install
|
||||
BUILD_ALLOW=1 bun run build # builds every plugin-designs/* bundle -> public/doxa-maps/
|
||||
bun run serve # optional: serves public/ at http://localhost:4173
|
||||
bun run serve # optional: serves public/ at http://localhost:4173
|
||||
```
|
||||
|
||||
The build writes `public/doxa-maps/manifest.json`. Each profile carries an
|
||||
`embed.iframe` snippet, e.g.:
|
||||
Each profile in `manifest.json` carries an `embed.iframe` snippet:
|
||||
|
||||
```html
|
||||
<iframe src="./all-people-groups/all-people-groups/index.html"
|
||||
|
|
@ -95,184 +94,114 @@ The build writes `public/doxa-maps/manifest.json`. Each profile carries an
|
|||
style="border:none;border-radius:12px;" allowfullscreen></iframe>
|
||||
```
|
||||
|
||||
URLs are root-relative, so the whole `doxa-maps/` folder is position-independent —
|
||||
drop it on Cloudflare/Vercel/any static host and every iframe resolves. (For an
|
||||
external absolute base, set `embedBaseUrl` in the build config before building.)
|
||||
**The folder is position-independent.** Snippet URLs are relative to the tree root, so `doxa-maps/`
|
||||
works dropped on any static host. For absolute URLs, set `embedBaseUrl` in the build config.
|
||||
|
||||
### 2. Mobile app — Android / Capacitor *(required)*
|
||||
### 2. Mobile app — Android / Capacitor
|
||||
|
||||
**Install:** Bun · **Android SDK** (`sdkmanager`, `adb` on your PATH) · **JDK 17**
|
||||
(the Gradle wrapper needs it) · Capacitor (`@capacitor/*` are already devDependencies,
|
||||
so `bun install` covers them). A connected device or running emulator for install.
|
||||
**Install:** Bun · **Android SDK** (`sdkmanager`, `adb` on your PATH) · **JDK 17** · a device or
|
||||
emulator. The `@capacitor/*` packages are already devDependencies.
|
||||
|
||||
```bash
|
||||
bun run check:mobile # CONTRACT check — needs NO JDK/SDK/device
|
||||
bun run phone # full path — needs the toolchain above
|
||||
```
|
||||
|
||||
**`check:mobile` is runnable anywhere.** `cap sync` is a file copy plus plugin wiring, so it
|
||||
needs no Android toolchain at all; `cap:verify` then asserts
|
||||
`deployment/capacitor/android/app/src/main/assets/public/index.html` exists. That guards the soft
|
||||
failure `capacitor.config.json` warns about — *a wrong `webDir` yields an EMPTY
|
||||
`assets/public` and a BLANK WHITE SCREEN at launch, not an error*. Verified 2026-09-20 with
|
||||
`java`, `gradle`, `adb` and `sdkmanager` all absent: sync EXIT=0, the whole `public/` tree
|
||||
staged into the native assets, `cap:verify` OK. Negative-tested by removing `index.html` — EXIT=1.
|
||||
|
||||
> **Why this says "the whole tree" and not a bundle count.** It used to read "21 bundles staged".
|
||||
> That number matched nothing measurable: the tree today has **18 built bundles**, **17** of them
|
||||
> listed in `manifest.json` (`key-cities` is withheld by `gatedProfiles`), and **19**
|
||||
> subdirectories under `public/doxa-maps/` — one of which is `assets/`, not a bundle at all.
|
||||
> `cap sync` copies `webDir` wholesale, so *bundles* was never the unit it operates on. A count
|
||||
> that names the wrong noun goes stale the moment the tree changes and cannot be re-checked,
|
||||
> which is exactly how the original figure survived unnoticed.
|
||||
|
||||
**Status: built + contract-proven; not shot.** The APK itself (`gradlew assembleDebug`) and
|
||||
its launch on a device still need JDK 17 + the Android SDK + `adb`.
|
||||
|
||||
`phone` chains the whole path: `apk:debug` (which runs `bun run build`, then
|
||||
`cap:sync` to copy the web output into the Android project, then `cap:verify`, then
|
||||
`./gradlew assembleDebug`) and finally `apk:install` (`adb install -r ...`) onto the
|
||||
device. Run the halves separately if you want to inspect between steps:
|
||||
|
||||
```bash
|
||||
bun run apk:debug # build the APK only
|
||||
bun run check:mobile # contract check — needs NO JDK, SDK or device
|
||||
bun run phone # the full path: build → cap:sync → cap:verify → gradlew assembleDebug → adb install
|
||||
bun run apk:debug # or the halves separately: build the APK only
|
||||
bun run apk:install # push the built APK to a connected device
|
||||
```
|
||||
|
||||
Only Android is scaffolded on this branch (`deployment/capacitor/android`, `capacitor.config.json`);
|
||||
iOS is not set up.
|
||||
**`check:mobile` runs anywhere.** It copies the built tree into the Android project and asserts
|
||||
`deployment/capacitor/android/app/src/main/assets/public/index.html` exists, which catches the one
|
||||
silent failure: a wrong `webDir` launches to a blank white screen, not an error. Only Android is
|
||||
scaffolded; iOS is not set up.
|
||||
|
||||
### 3. Direct-JS / dashboard *(required)*
|
||||
### 3. JavaScript embed
|
||||
|
||||
**Install:** Bun.
|
||||
|
||||
```bash
|
||||
BUILD_ALLOW=1 bun run build
|
||||
```
|
||||
|
||||
Then paste the profile's `embed.script` snippet from `manifest.json` — a `<script src>`
|
||||
with a Subresource-Integrity (SRI) hash plus the custom-element tag:
|
||||
**Install:** Bun. Build, then paste the plugin's `embed.script` snippet from `manifest.json`: a
|
||||
`<script src>` with a Subresource-Integrity hash, plus the custom element.
|
||||
|
||||
```html
|
||||
<script src="./all-people-groups/all-people-groups.js"
|
||||
integrity="sha384-j5Ypx+EI9DGlTClzgLs1R8wAnmTnkQ7HgAhHrxyGoVoHDMTlFVY7slU6JNicn77T"
|
||||
crossorigin="anonymous"></script>
|
||||
integrity="sha384-…" crossorigin="anonymous"></script>
|
||||
<all-people-groups
|
||||
profile-config='{"profile":"all-people-groups","tk":"YOUR_MAPBOX_TOKEN"}'>
|
||||
</all-people-groups>
|
||||
```
|
||||
|
||||
The element accepts props / emits events, so it can wire into a surrounding page as a
|
||||
unified research dashboard (deploy-3 proof). The SRI hash regenerates on every build.
|
||||
**The hash changes on every build**, so always copy it from the current `manifest.json`. The element
|
||||
takes props and emits events, so it can wire into the surrounding page.
|
||||
|
||||
#### Embedding on a FOREIGN host (a partner site) — the two things that differ
|
||||
**On a partner's site, a JS embed needs two values an iframe does not.** An iframe carries its own
|
||||
document base; a script runs in the host's document, so relative asset and API paths would resolve
|
||||
against the host and 404. The snippet already ships both:
|
||||
|
||||
Both embed methods were run to their first failure on a foreign host page (2026-09-20).
|
||||
**deploy-1 needed nothing**; **deploy-3 needed two values**, and the snippet now ships both.
|
||||
|
||||
*Why they differ:* an **iframe carries its own document base**, so the leaf page's relative
|
||||
asset and API paths resolve correctly. A **direct-JS embed executes in the host's document**,
|
||||
so those same relative paths resolve against the *host* — and 404. Same bundle, same data,
|
||||
different containing document.
|
||||
|
||||
| | iframe (deploy-1) | direct-JS (deploy-3) |
|
||||
| | iframe | JavaScript embed |
|
||||
|---|---|---|
|
||||
| `assetsBase` | not needed | **required** — in `profile-config` |
|
||||
| API base | not needed | **required** — `window.MAP_APP_API_URL` (a host global, *not* a profile-config key) |
|
||||
| Mapbox token | inherited by the leaf | replace `YOUR_MAPBOX_TOKEN` |
|
||||
| `assetsBase` | not needed | **required**, in `profile-config` (`YOUR_TREE_URL/assets` until you set `assetsBaseUrl`) |
|
||||
| API base | not needed | **required**, `window.MAP_APP_API_URL` (a host global) |
|
||||
| Mapbox token | inherited by the leaf page | replace `YOUR_MAPBOX_TOKEN` |
|
||||
|
||||
Both are emitted into `embed.script` already: the API base uses the configured value, and
|
||||
`assetsBase` falls back to a `YOUR_TREE_URL/assets` placeholder when `assetsBaseUrl` is unset.
|
||||
### 4. WordPress / Disciple Tools — proof of concept
|
||||
|
||||
**One rewrite either way.** Snippet URLs are **tree-relative** (`./bundle/...`) because
|
||||
`embedBaseUrl` is empty by default — that is what keeps a dropped folder position-independent
|
||||
and the `check:drop` gate green. A partner whose page is not *at* the tree root must either
|
||||
prefix the path (`./doxa-maps/...`) or set **`embedBaseUrl`** in the build config to emit
|
||||
absolute URLs. That is a deployment choice, not a defect.
|
||||
**No plugin needed for the simple path:** build, then paste the iframe or script snippet into a
|
||||
WordPress **Custom HTML** block.
|
||||
|
||||
### 4. WordPress *(built + contract-proven; not yet shot on a live WP install)*
|
||||
|
||||
> **Corrected 2026-09-20.** This section previously read *"deferred POC — no dedicated
|
||||
> WordPress build step exists on this branch"*. That is no longer true: a first-class
|
||||
> plugin exists at `deployment/wordpress/doxa-maps-embed/` (a `[doxa_map]` shortcode + a Gutenberg
|
||||
> block), and its emitted markup is now verified by a gate.
|
||||
|
||||
**The simple path** — no plugin needed, same artifacts as methods 1 and 3:
|
||||
|
||||
```bash
|
||||
BUILD_ALLOW=1 bun run build
|
||||
```
|
||||
|
||||
Then paste either the iframe snippet **or** the `<script src> + SRI` snippet from
|
||||
`manifest.json` into a WordPress **Custom HTML** block.
|
||||
|
||||
**The plugin path** — `deployment/wordpress/doxa-maps-embed/` registers the bundle through WordPress's
|
||||
own dependency graph, so the SRI hash and `crossorigin` survive and the script loads once
|
||||
per page rather than once per embed.
|
||||
**The plugin path:** `deployment/wordpress/doxa-maps-embed/` adds a `[doxa_map]` shortcode and a
|
||||
Gutenberg block, registering the bundle through WordPress's own dependency graph so the SRI hash and
|
||||
`crossorigin` survive and the script loads once per page.
|
||||
|
||||
```bash
|
||||
bun run check:wp-embed # renders the REAL shortcode with no WordPress installed
|
||||
```
|
||||
|
||||
That gate stubs the ~20 WordPress functions the plugin actually calls (`deployment/wordpress/tests/`),
|
||||
renders the shortcode against the REAL built `manifest.json`, and asserts the contract
|
||||
`HOW-TO-TEST.md` defines: the element tag is emitted, `profile-config` decodes to valid
|
||||
JSON, the script `src` equals the catalog path, the SRI `integrity` is injected by the
|
||||
`script_loader_tag` filter, `crossorigin="anonymous"` is present — plus a negative test
|
||||
that a **malformed** integrity is rejected.
|
||||
**What is proven, and what is not.** The check renders the shortcode against the real `manifest.json`
|
||||
and asserts the element tag, valid `profile-config` JSON, the script `src`, the injected SRI hash and
|
||||
`crossorigin` — and rejects a malformed hash. It does not prove that WordPress renders the page or a
|
||||
browser runs it; that needs a live install (`wp-env start`, see
|
||||
`deployment/wordpress/doxa-maps-embed/HOW-TO-TEST.md`).
|
||||
|
||||
**Status honesty — say "contract-proven", not "proven".** What is proven: the plugin emits
|
||||
the correct markup, repeatably and in CI. What is *not*: that WordPress renders the page,
|
||||
that the block editor registers it, or that a browser executes the result. Those still need
|
||||
a real WP install (`wp-env start` — see `deployment/wordpress/doxa-maps-embed/HOW-TO-TEST.md`).
|
||||
### 5. Desktop — Tauri — proof of concept
|
||||
|
||||
### 5. Desktop — Tauri *(built, not shot — a binary and a `.deb` ARE emitted; only the GUI launch is unverified)*
|
||||
|
||||
**Install:** Bun ALONE for the contract check. For the binary: the Rust toolchain (`rustup`),
|
||||
the Tauri CLI (`cargo install tauri-cli`), and the platform's system webview libs.
|
||||
**Install:** Bun for the check. For the binary: Rust (`rustup`), the Tauri CLI
|
||||
(`cargo install tauri-cli`) and the platform's webview libraries.
|
||||
|
||||
```bash
|
||||
bun run check:tauri # CONTRACT check — needs NO Rust, NO Tauri CLI
|
||||
cargo tauri build # the binary — needs the toolchain above
|
||||
bun run check:tauri # contract check — needs NO Rust, NO Tauri CLI
|
||||
bun run tauri:build # the binary and a .deb — needs the toolchain above
|
||||
```
|
||||
|
||||
**What "built, not shot" means here, measured rather than asserted.** `cargo tauri build` has been run
|
||||
and its outputs are on disk: `deployment/src-tauri/target/release/doxa-maps-desktop` is a 67 MB ELF
|
||||
64-bit executable, and `target/release/bundle/deb/` holds a 60 MB `.deb`. Eight source files are
|
||||
git-tracked under `deployment/src-tauri/`. What has *not* happened is a window opening: this build box
|
||||
is headless with no Xvfb, so nothing has confirmed the app paints rather than launching to a blank
|
||||
frame. Say "built, not shot" — not "proven", and not "aspirational".
|
||||
**The desktop app wraps the same tree.** `deployment/src-tauri/tauri.conf.json` points
|
||||
`build.frontendDist` at `../public`, the desktop twin of Capacitor's `webDir`; the Rust crate carries
|
||||
no application logic. `check:tauri` asserts that path resolves, every window `url` exists in the built
|
||||
tree, the identifier matches Capacitor's `appId`, and the crate files are present.
|
||||
|
||||
`deployment/src-tauri/tauri.conf.json` + the `deployment/src-tauri/` Rust crate wrap the **same `public/` tree**
|
||||
Capacitor and the CDN serve — `build.frontendDist` is `../public`, the desktop counterpart of
|
||||
Capacitor's `webDir`. One `bun run build` now feeds four consumers: CDN, Capacitor, local
|
||||
staging, and Tauri. The crate carries no application logic; the product is the web tree.
|
||||
**Token caveat:** a Tauri webview serves from `tauri://localhost` (or `http://tauri.localhost` on
|
||||
Windows), so a `tokenUrl` endpoint must send CORS headers for that origin. The fix is a response header
|
||||
on the token host, not a change in `tauri.conf.json`. Details:
|
||||
[`deployment/src-tauri/README.md`](../deployment/src-tauri/README.md).
|
||||
|
||||
**`check:tauri` is runnable anywhere**, exactly like `check:mobile`. It asserts the config points
|
||||
at files that exist: `frontendDist` resolves (Tauri resolves it relative to `deployment/src-tauri/` — the
|
||||
easiest thing to get wrong), every window `url` exists in the built tree, the identifier matches
|
||||
Capacitor's `appId`, and the three crate files are present. That guards the soft failure Tauri
|
||||
shares with Capacitor — *a wrong path yields a BLANK WHITE WINDOW at launch, not an error*.
|
||||
Negative-tested five ways (wrong `frontendDist`, missing window url, identifier drift, empty
|
||||
windows array, missing crate file); each exits 1.
|
||||
---
|
||||
|
||||
> **The token/CORS caveat, stated before you hit it.** A Tauri webview serves from a custom origin
|
||||
> (`tauri://localhost`, or `http://tauri.localhost` on Windows), *not* from your token host. The
|
||||
> `tokenUrl` endpoint must send CORS headers permitting that origin — the same surprise
|
||||
> `capacitor.config.json` documents for `https://localhost`. **The fix is a response header on the
|
||||
> token host, not a change in `tauri.conf.json`.**
|
||||
## Notes and history
|
||||
|
||||
**Status: BUILT — artifacts emitted; launch unverified.** Run 2026-09-21 with the real toolchain
|
||||
(`tauri info` all ✔: webkit2gtk 2.50.6, rustc/cargo 1.96.1, frontendDist `../public`, Vue.js/Vite):
|
||||
`bun run tauri:build` produced a **65 MB ELF executable** and a **58 MB `.deb`**
|
||||
(`Package: doxa-maps`, `Depends: libwebkit2gtk-4.1-0, libgtk-3-0`). The web tree is compiled INTO
|
||||
the binary — 19 distinct `doxa-maps/...` asset paths are embedded in the ELF, which is why the
|
||||
`.deb` holds only 6 entries.
|
||||
The measurements behind the sections above.
|
||||
|
||||
`bundle.targets` is `["deb"]`, not `"all"`: `rpmbuild` and `linuxdeploy` (AppImage) are absent on
|
||||
this box, so `"all"` would fail two of three formats at the very end of a ~6-minute release build.
|
||||
|
||||
Still **not "proven"** — say *built, not shot*. The GUI launch is unverified because this VPS is
|
||||
headless: the binary panics in `tao` with "Failed to initialize GTK" (no display, not a defect) and
|
||||
no Xvfb is installed. See [`deployment/src-tauri/README.md`](../deployment/src-tauri/README.md) for placement rules, the
|
||||
token/CORS caveat and the full artifact table.
|
||||
- **Mobile — `check:mobile` needs no toolchain (verified 2026-09-20).** With `java`, `gradle`, `adb` and
|
||||
`sdkmanager` all absent, sync exited 0, the whole `public/` tree was staged into the native assets and
|
||||
`cap:verify` passed; removing `index.html` made it exit 1. The doc once said "21 bundles staged"; that
|
||||
count named the wrong unit (`cap sync` copies `webDir` wholesale) and went stale, so it says "the whole
|
||||
tree" now.
|
||||
- **JavaScript embed — foreign-host run (2026-09-20).** Both embed methods were run on a foreign host page
|
||||
until they failed: the iframe needed nothing; the script embed needed `assetsBase` and the API base,
|
||||
which the snippet now emits. Same-root and foreign-host both then passed (map painted, attribution
|
||||
shown, 0 errors, 0 404s).
|
||||
- **WordPress — corrected 2026-09-20.** This section once said no WordPress build step existed; the
|
||||
shortcode + block plugin and its contract check were added after. The check stubs the ~20 WordPress
|
||||
functions the plugin calls (`deployment/wordpress/tests/`).
|
||||
- **Tauri — the build ran (2026-09-21).** With the real toolchain (`tauri info` all ✔: webkit2gtk 2.50.6,
|
||||
rustc/cargo 1.96.1) `bun run tauri:build` produced a 65 MB ELF executable and a 58 MB `.deb`
|
||||
(`Depends: libwebkit2gtk-4.1-0, libgtk-3-0`); a later run measured 67 MB and 60 MB. The web tree is
|
||||
compiled into the binary, which is why the `.deb` holds only 6 entries. `bundle.targets` is `["deb"]`
|
||||
because `rpmbuild` and `linuxdeploy` were absent, so `"all"` would fail two formats at the end of a
|
||||
~6-minute build. The window has not been opened: the build box is headless, and the binary stops with
|
||||
"Failed to initialize GTK" (no display, not a defect). `check:tauri` is negative-tested five ways
|
||||
(wrong `frontendDist`, missing window url, identifier drift, empty windows array, missing crate file).
|
||||
|
|
|
|||
Loading…
Reference in a new issue