doxa-maps/docs/architecture/embeddability.md

3 KiB

Architecture — the embeddability design (generated pages + manifest)

The simple recipes are in ../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
└── <bundle>/
    ├── <bundle>.js                 ← the bundle IIFE (the island)
    ├── index.html                  ← per-bundle staging/showcase page
    ├── <profile>/index.html        ← ★ standalone page per profile — link it or iframe it
    └── <instance>/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 <profile>/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 ../<bundle>.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 <iframe src=".../<profile>/index.html" …> snippet
  • embed.script — the <script src> + <element profile-config='…'> pair

Hosts, staging pages, and the share button all read this one catalog — snippets are generated once, never hand-written.

Two embed modes, one rule about iframes

Mode What When
script + element host page loads the IIFE, drops the tag full integration: host styles/sizes it, passes config
iframe / direct link point at the generated <profile>/index.html zero-effort embeds, CMSs that only allow iframes, shared links

Both are supported on built pages. The one "never iframe" rule applies only to the dev staging script (internal/staging.js), which mounts elements directly because a dev-server iframe crosses realms (instanceof breaks). Built pages have no such problem — the generated staging (staging-forge.js) itself uses an iframe stage.