From f0ceefa6dbb5d0d3839deb79aa710fb4b339d8b5 Mon Sep 17 00:00:00 2001 From: doxa-maps Date: Fri, 11 Sep 2026 18:37:45 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20fix=20cold-read=20gaps=20=E2=80=94=20em?= =?UTF-8?q?beddability=20design=20doc=20(generated=20pages=20+=20manifest)?= =?UTF-8?q?,=20SPA=20recipe=20+=20PROFILES=20contract,=20copy-rename=20che?= =?UTF-8?q?cklist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app-profiles/template-bundle/README.md | 10 ++++- docs/architecture/README.md | 3 +- docs/architecture/app-wrappers.md | 28 +++++++++++++ docs/architecture/embeddability.md | 57 ++++++++++++++++++++++++++ docs/how-to.md | 44 ++++++++++++++++++++ docs/reference.md | 9 +++- docs/tutorial.md | 4 +- 7 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 docs/architecture/embeddability.md diff --git a/app-profiles/template-bundle/README.md b/app-profiles/template-bundle/README.md index 80f3b5a0e..6b4faea08 100644 --- a/app-profiles/template-bundle/README.md +++ b/app-profiles/template-bundle/README.md @@ -17,8 +17,14 @@ needs it — an expert move, not a first move. To see everything `library/` offe ## Build a new map — the steps -1. **Copy this folder** → `app-profiles//`. The folder name becomes your bundle name (and - output file `.js`). Rename the custom-element tag in `index.js` to your map's tag. +1. **Copy this folder** → `app-profiles//`, then change **exactly four things**: + - the **folder name** (= bundle name = output file `.js`), + - the **tag** in `index.js` (`customElements.define('', …)`), + - the **tag + `profile` strings** in `index.html` (the staging page must mount your tag), + - the **`.vue` filenames** — rename `template-map-a.vue` to your screen's name and delete + `template-map-b.vue` if you only want one screen (filename = the `profile` name). + Nothing else. Check it: `bun run dev` → your map renders at + `http://localhost:5173/app-profiles//index.html`. 2. **List what your map needs** — data, colors, controls (legend, search, share, fullscreen…). 3. **Pick pieces from `../../docs/reference.md`** and import them by reference (`@map/…`) — never copy library code into your folder. diff --git a/docs/architecture/README.md b/docs/architecture/README.md index a5151b6d6..6aa5716a4 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -10,7 +10,8 @@ when a simple doc says *"full contract in architecture/…"*, this is where it p | [color-strategies.md](./color-strategies.md) | the color-strategy contract + legend wiring | | [components.md](./components.md) | shared component catalog + shadow-DOM conventions | | [i18n.md](./i18n.md) | the locale contract + add-a-language checklist | -| [app-wrappers.md](./app-wrappers.md) | the SPA shell / sidebar `nav.js` contract | +| [embeddability.md](./embeddability.md) | the generated per-map pages + manifest that make every map embeddable by default | +| [app-wrappers.md](./app-wrappers.md) | the SPA shell: `nav.js` + `PROFILES` contracts, `bun run shell` pipeline | | [poster-generation.md](./poster-generation.md) | the poster/preview generator pipeline | Simple docs: [../why.md](../why.md) · [../tutorial.md](../tutorial.md) · [../how-to.md](../how-to.md) · diff --git a/docs/architecture/app-wrappers.md b/docs/architecture/app-wrappers.md index 8ab672ba7..ad6fd05b9 100644 --- a/docs/architecture/app-wrappers.md +++ b/docs/architecture/app-wrappers.md @@ -63,3 +63,31 @@ python3 -m http.server 4173 --bind 0.0.0.0 Then open it and narrow the window below 1024px (or use the browser device toolbar) to check the mobile behaviour. + +## The `PROFILES` registry (the wrapper's map catalog) + +Beside `nav.js`, the wrapper's `index.html` declares `PROFILES` — one entry per mountable map: + +```js +const PROFILES = { + 'prayer-progress': { + tag: 'doxa-map', // the element to mount (a bundle may register several) + src: './doxa-maps/doxa-simple-map/doxa-simple-map.js', // the bundle IIFE, tree-relative + views: ['map'], // which sub-views (map/list/tree) this profile offers + // + the profile-config the wrapper passes (copied from the generated instance page's BASE_CONFIG) + }, +} +``` + +`nav.js` ids route to `PROFILES` keys; the wrapper lazy-loads `src` on first visit and mounts +`tag`. Gotchas: an instance page's tag may differ from the bundle name (`doxa-simple-map` +registers `` too), and `colorStrategy` values are registry keys derived from +FILENAMES (`prayer-progress.js` → `'prayerProgress'`). + +## How a wrapper becomes the served SPA + +`bun run shell` (`internal/install-shell.mjs`) **copies** `layouts//` to the root of +`public/`, so `public/index.html` is the shell and `public/doxa-maps/…` are the bundles it +mounts — one served tree, two roles. Copy (not symlink) keeps Railway/Capacitor/file:// happy; +`--no-token` omits the local-only `token.local.js` (mandatory for release builds). Capacitor +also *requires* this root `index.html` (`cap copy` hard-fails without it). diff --git a/docs/architecture/embeddability.md b/docs/architecture/embeddability.md new file mode 100644 index 000000000..2ba40f03c --- /dev/null +++ b/docs/architecture/embeddability.md @@ -0,0 +1,57 @@ +# Architecture — the embeddability design (generated pages + manifest) + +> The simple recipes are in [../how-to.md](../how-to.md). This is the design that makes +> **every map embeddable by default**: the build generates a self-contained page per map. + +## What `bun run tree` generates + +After the per-bundle vite builds, `internal/generate-tree.mjs` writes the served tree: + +``` +public/doxa-maps/ +├── index.html ← staging root (semantic tree of every map) +├── manifest.json ← machine-readable catalog + ready-made embed snippets +└── / + ├── .js ← the bundle IIFE (the island) + ├── index.html ← per-bundle staging/showcase page + ├── /index.html ← ★ standalone page per profile — link it or iframe it + └── /index.html ← ★ same, per parameterized instance (e.g. prayer/) +``` + +The ★ pages are the **embeddable-by-default surface**: every `.vue` profile (and every +instance in a `*.instances.json`) automatically gets a directly linkable, iframe-able URL — +no per-map work. + +## Anatomy of a generated profile page + +Each `/index.html` is a **bare full-page map** — no chrome, so the URL *is* the map +("Share shares THE MAP"). It loads: the Mapbox GL peer (once, same realm), the bundle IIFE via +a relative `../.js`, and mounts the element with that profile's config. The token +resolves by the ladder (prop → `?tk=` → baked → `window.MAP_TOKEN_URL` → `tokenUrl` → +same-origin `/api/maps/token`), fetched at page load — tokens are treated as data, never eval'd. + +All URLs stay **relative** unless `embedBaseUrl` is set, so the whole tree can be dropped under +any web-root or subpath and keeps working (`check:drop` gates this). + +## The manifest — embed snippets as data + +`manifest.json` (`doxa-maps-manifest@1`) lists every bundle → profiles, each with `element`, +`scriptUrl`, `iframeUrl`, and a ready-made `embed` object: + +- `embed.iframe` — a paste-ready ` +``` + +You never write these snippets by hand — `public/doxa-maps/manifest.json` lists a ready-made +`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). + +--- + ## Add a new map profile The fastest path is **copy `template-bundle` and rename it.** A bundle is one folder under @@ -175,6 +198,27 @@ snippets (leave it empty for relative URLs that work wherever the folder is drop --- +## Compose maps into a SPA (the app shell) + +Several maps + one layout = a single-page app, served from the same `public/` tree. + +1. **Pick a wrapper** in `layouts/` (`app-wrapper-material` is the full shell: sidebar, + lazy-loading, i18n). +2. **Register your map** in two places inside the wrapper: + - `nav.js` — add a sidebar entry (`{ kind, id, label, icon, target }`; `target` controls + desktop/mobile packaging). + - the `PROFILES` object in its `index.html` — tell the shell what to mount: + `{ tag: '', src: './doxa-maps//.js', views: ['map'] }`. +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.) +4. **Build & serve**: `bun run build`, then serve `public/` — the shell lazy-loads each bundle + the first time you open its nav entry. + +Contracts and gotchas (PROFILES shape, tag-vs-bundle names, `--no-token`): +[architecture/app-wrappers.md](./architecture/app-wrappers.md). + +--- + ## Build the Android / iOS app (optional) **Mobile is separate from `bun run build`.** The default build produces **only** the `doxa-maps` diff --git a/docs/reference.md b/docs/reference.md index d41be41bb..5c86d08c8 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -127,6 +127,12 @@ CSS is injected by JS (`cssCodeSplit: false`), so there is no separate `.css`. T name comes from `vite.doxa-maps-build-config.json` `name` (default `doxa-maps`). `emptyOutDir: false` keeps sibling bundles intact when you rebuild one. +**What a built bundle folder contains.** Beyond the IIFE, `bun run tree` generates an +`index.html` **per bundle** (staging/showcase), **per profile**, and **per instance** — +each profile/instance page is a standalone, directly linkable/iframe-able map — plus +`public/doxa-maps/manifest.json` with ready-made `embed.iframe`/`embed.script` snippets for +every profile. Full design: [architecture/embeddability.md](./architecture/embeddability.md). + **Bun is required.** `bun.lock` is the committed lockfile; `bun x vite` runs project-local Vite. `npm run …` resolves the same scripts but Bun must be on PATH. @@ -212,7 +218,8 @@ Simple lookups live here; the full contracts, catalogs, and wiring detail live i | [color-strategies.md](./architecture/color-strategies.md) | the strategy contract + legend wiring | | [components.md](./architecture/components.md) | shared component catalog + conventions | | [i18n.md](./architecture/i18n.md) | the locale contract + add-a-language checklist | -| [app-wrappers.md](./architecture/app-wrappers.md) | the SPA shell / sidebar `nav.js` contract | +| [embeddability.md](./architecture/embeddability.md) | generated per-map pages + manifest — embeddable by default | +| [app-wrappers.md](./architecture/app-wrappers.md) | the SPA shell: `nav.js` + `PROFILES` contracts | | [poster-generation.md](./architecture/poster-generation.md) | the poster/preview generator pipeline | The **Semantic Tree Legend** component (a reusable hierarchical legend) is documented in diff --git a/docs/tutorial.md b/docs/tutorial.md index a384273c3..9e50a1d65 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -119,7 +119,9 @@ change. Conversation in, map out. ```bash cp -r app-profiles/template-bundle app-profiles/harvest-map # 1. copy the starter -# 2. rename and edit the files inside to be your map (or let the agent do it while you describe) +# 2. change exactly four things: the folder name, the tag in index.js, the tag/profile +# strings in index.html, and the .vue filenames (delete the second one if you want one +# screen). Full checklist: app-profiles/template-bundle/README.md # 3. it shows up in staging automatically — no list to touch bun run build # 4. when ready, publish ```