README for vibe coders: the Goal, pictures first, everything else folded away
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions

- The Goal and Isaiah 40:3 lead the README, byte-for-byte as written on the
  public repo (driver-web-edits.patch), so the next export keeps them.
- Each of the 5 capabilities and 5 deployment methods is now a one-line
  summary, its picture, and a collapsed "More about…" dropdown. 16 dropdowns,
  none open by default, so a reader can scroll the pictures and summaries.
- WordPress / Disciple Tools and desktop say what is true in plain words: we got
  a JAMstack map running there; proven, not yet a production feature; the
  Research Gallery could become a strong desktop app. Their cards match.
- The agent-swarm section explains the problem in plain words (one shared
  library and one build means one bad change breaks every map), with a git
  glossary and the commands folded away.
- Get it running now includes your own free Mapbox token (the public copy
  ships without one), and a dropdown on changing the studio itself.
- The diagram has eight plugin nodes, main at the centre behind a padlocked
  merge-queue ring, and a plain-language "How main is protected" strip.
This commit is contained in:
doxa-maps-88655c70 2026-09-25 05:44:08 +00:00
parent 8423411757
commit 4314f0e2f6
7 changed files with 370 additions and 126 deletions

330
README.md
View file

@ -1,3 +1,9 @@
# Goal
Made by Vibe Coders for Vibe Coders to prepare the way of the Lord and make His paths straight.
Isaiah 40:3 “...Prepare the way for the LORD in the wilderness; make a straight highway for our God in the desert."
# dx-maps — one build, maps that embed everywhere
**Write a map once in `plugin-designs/<your-map>/`, run one build, and drop it on any site** —
@ -12,22 +18,40 @@ capabilities, five deployment methods, one shared library, one build.
| **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` |
| **Shared library, private plugins** — reuse without breaking each other | `bun run check:library-index` |
| **One agent per plugin, one shepherd, one green `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) |
*Click any ▸ to read more. Everything below is folded so you can scroll the pictures first.*
---
## The 5 plugin capabilities
A capability is **how much a plugin nests**, from one element (1) to an app of apps (5). Every
screenshot is a real bundle from this repo's build.
screenshot is a real plugin from this repo's build.
### Capability 5 — app shell: the Research Gallery
One plugin, one element (`<doxa-maps-spa>`), and every tab inside it is a real plugin from a lower
tier that you can still embed on its own. `plugin-designs/doxa-maps-spa/`
**One plugin that holds every map as a tab** — and each tab is still a plugin you can embed on its own.
![Capability 5 — the Research Gallery app shell (doxa-maps-spa)](documentation/readme-images/cap5-doxa-maps-spa.png)
<details>
<summary><b>More about capability 5 — the app shell</b></summary>
An app shell is the outer frame of a whole app: a rail of tabs down the side, a header, and
navigation. Each tab loads a **real plugin** from a lower capability, only when you open it, so the
app stays fast. Nothing is copied into the shell: when a plugin improves, the shell shows the
improvement on the next build.
- **Where it lives:** `plugin-designs/doxa-maps-spa/` — one element, `<doxa-maps-spa>`.
- **Changing the tabs:** the order and grouping are one list, `NAV_MODEL` in `doxa-maps-spa.vue`.
- **New plugins show up by themselves:** any plugin that isn't placed in a section lands on the
folded **Experimental** shelf, read from the built `manifest.json` — no code to edit.
- **A second app shell ships too:** the Plugin Studio (below),
which holds *every* plugin for developers.
</details>
<details>
<summary><b>What the Research Gallery is made of — seven production maps, an Experimental shelf, all five capabilities in one plugin</b></summary>
@ -63,36 +87,90 @@ Research Gallery <doxa-maps-spa> capability 5
### Capability 4 — composite: plugins wired together
Nests lower-tier plugins and wires them with props and events. `plugin-designs/my-upg-100-list/`
**Several plugins glued into one**, talking to each other — and each piece still embeddable alone.
![Capability 4 — the UPG-100 composite (my-upg-100-list)](documentation/readme-images/cap4-upg100-mfe.png)
| List (cap 1, embeddable alone) | Poster wall (cap 1, embeddable alone) |
| List (capability 1, embeddable alone) | Poster wall (capability 1, embeddable alone) |
|---|---|
| ![UPG-100 list leaf](documentation/readme-images/cap4-upg100-list.png) | ![UPG-100 poster-wall leaf](documentation/readme-images/cap4-upg100-poster-wall.png) |
### Capability 3 — multi-view: many views, one element
<details>
<summary><b>More about capability 4 — composites</b></summary>
`plugin-designs/doxa-research-map/`
A composite takes plugins that already exist and places them together — side by side, stacked, or
as views you switch between — and ships the set as **one new element**. The pieces talk through
**props** (settings passed in) and **events** (messages sent out): click a region in one piece and
another piece updates. Nothing is copied, so each piece can still be embedded on its own.
- **Example:** `plugin-designs/my-upg-100-list/` — the UPG-100 list and its poster wall, joined into
`upg-100-mfe`. Big Picture, Simple Research and Regions are composites too.
- **How to declare one:** a `<name>.meta.json` beside it with `"type": "nested-elements-mfe"` and a
`"composes"` list of the plugins inside.
- **The layouts that do the placing:** `VerticalTabsLayout` and `StackLayout` in `library/layouts/`.
</details>
### Capability 3 — multi-view: many screens, one element
**One plugin with several screens inside** — tabs you switch between.
![Capability 3 — the monolithic research map (doxa-research-map)](documentation/readme-images/cap3-monolithic-research.png)
<details>
<summary><b>More about capability 3 — multi-view</b></summary>
All the screens live in one plugin, so they share code and data and switch instantly. The trade-off:
you embed the whole thing — you can't embed just one of its tabs. When you need that, split the tabs
into separate plugins and join them with a composite (capability 4).
- **Example:** `plugin-designs/doxa-research-map/` — Prayer, Engagement, People Groups, Regions,
Languages and Religions in one map.
- **How to declare one:** a `<name>.meta.json` with `"type": "monolithic"`.
</details>
### Capability 2 — presets: one plugin, many maps
One `root.vue` plus a preset table. `plugin-designs/doxa-simple-map/`
**The same map code, several ready-made versions** — each with its own page and embed code.
| Prayer | Engagement | Adoption |
|--------|------------|----------|
| ![Capability 2 — prayer preset (doxa-simple-map)](documentation/readme-images/cap2-prayer.png) | ![Capability 2 — engagement preset (doxa-simple-map)](documentation/readme-images/cap2-engagement.png) | ![Capability 2 — adoption preset (doxa-simple-map)](documentation/readme-images/cap2-adoption.png) |
<details>
<summary><b>More about capability 2 — presets</b></summary>
You write one map, then list its versions in a small table: a name and the settings for each. The
build turns every row into its own standalone page and embed snippet — same code, different map.
Adding a version means adding a row, not writing code.
- **Example:** `plugin-designs/doxa-simple-map/` — Prayer, Engagement and Adoption from one map.
- **How to make one:** add `<name>.instances.json` beside the plugin:
`{ "instances": [{ "id": "prayer", "title": "Prayer", "props": { … } }] }`.
</details>
### Capability 1 — single element
One folder, one `.vue`, one element. `plugin-designs/all-people-groups/`
**One folder, one map, one tag** you can paste into any page.
![Capability 1 — a single-element plugin (all-people-groups)](documentation/readme-images/cap1-single-element.png)
> Which bundle sits at which tier is generated, not remembered:
> [documentation/reference-capability-exemplars.md](./documentation/reference-capability-exemplars.md).
<details>
<summary><b>More about capability 1 — single element</b></summary>
The simplest plugin, and where every map starts: a folder with three files — the map's `.vue`, an
`index.js` that registers its tag, and an `index.html` for previewing it. The build turns it into
one custom HTML tag, like `<all-people-groups>`, that works on any web page.
- **Example:** `plugin-designs/all-people-groups/` — every people group on one map.
- **How to make one:** copy `plugin-designs/template-bundle/` to `plugin-designs/<your-map>/` and
rename it. The studio finds it automatically.
- **Which plugin is the example of each capability** is generated, not remembered:
[documentation/reference-capability-exemplars.md](./documentation/reference-capability-exemplars.md).
</details>
---
@ -100,28 +178,62 @@ One folder, one `.vue`, one element. `plugin-designs/all-people-groups/`
### 1. iframe — the prayer map on any site ✅ production
One `<iframe>` tag, no build step. Each plugin's exact snippet is `embed.iframe` in `manifest.json`:
**Paste one line of HTML and the map appears** — on any website, no coding.
![Method 1 — the prayer map embedded in a host page's iframe](documentation/readme-images/deploy1-iframe.png)
<details>
<summary><b>More about method 1 — iframe</b></summary>
An iframe is a window in your page that shows another page — here, the map's own page. It's the
easiest way to share a map: it works on any site that accepts HTML, and the map keeps its own look.
Every plugin's exact snippet is `embed.iframe` in `public/doxa-maps/manifest.json`:
```html
<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>
```
![Method 1 — the prayer map embedded in a host page's iframe](documentation/readme-images/deploy1-iframe.png)
Host the built `public/doxa-maps/` folder anywhere (it works from any address), then point the
iframe at it.
</details>
### 2. Mobile app — UPG-100 on a phone ✅ built
The UPG-100 prayer lists and posters, shipped on their own as a Capacitor app: one poster per row,
print options that wrap to fit. `bun run check:mobile` · `bun run phone` (needs Android SDK + JDK 17)
**The same maps, wrapped as a phone app.**
<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>
<details>
<summary><b>More about method 2 — mobile app</b></summary>
[Capacitor](https://capacitorjs.com) wraps the built maps into an Android app — the same files,
no new code. Shown here: the UPG-100 prayer lists and posters, one poster per row, with print
options that fit a phone.
```bash
bun run check:mobile # checks the app is wired up correctly — needs no Android tools
bun run phone # builds the app and installs it on a phone or emulator
```
`bun run phone` needs the Android SDK, JDK 17 and `adb`. Only Android is set up; iOS is not yet.
</details>
### 3. JavaScript embed — the Research Gallery on dx-life ✅ production
The whole capability-5 app on the dx-life research page, from one `<script src>` and one element —
replacing the separate research map, list and regions dashboards the page used to carry. The exact
snippet is `embed.script` in `manifest.json`; copy its `integrity` hash from there, as it changes with
every build:
**Two lines of code put a whole app inside your own page** — here, the Research Gallery on dx-life.
![Method 3 — the Research Gallery JS-embedded on the dx-life research page](documentation/readme-images/deploy3-js-embed-dxlife.png)
<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>
@ -130,93 +242,187 @@ every build:
<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>
```
![Method 3 — the Research Gallery JS-embedded on the dx-life research page](documentation/readme-images/deploy3-js-embed-dxlife.png)
</details>
### 4. WordPress / Disciple Tools plugin — 🧪 proof of concept
### 4. WordPress / Disciple Tools — 🧪 proven, not yet in production
A shortcode + block plugin in `deployment/wordpress/`; its contract test passes
(`bun run check:wp-embed`). Proven, not production-ready.
**We got a JAMstack map running inside WordPress and Disciple Tools.**
![Method 4 — WordPress / Disciple Tools plugin, proof of concept](documentation/readme-images/deploy4-wordpress.png)
![Method 4 — WordPress / Disciple Tools, proven but not yet in production](documentation/readme-images/deploy4-wordpress.png)
### 5. Desktop app (Tauri) — 🧪 proof of concept
<details>
<summary><b>More about method 4 — WordPress / Disciple Tools</b></summary>
A desktop binary and `.deb` build from `deployment/src-tauri/` (`bun run check:tauri`). Proven,
not production-ready.
It works, but it isn't yet a production feature of the map bundler. Two ways to try it:
![Method 5 — desktop app (Tauri), proof of concept](documentation/readme-images/deploy5-desktop.png)
- **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.**
![Method 5 — desktop app (Tauri), proven but not yet in production](documentation/readme-images/deploy5-desktop.png)
<details>
<summary><b>More about method 5 — desktop app</b></summary>
[Tauri](https://tauri.app) wraps the same built maps into a desktop program: it produced a Linux
app and a `.deb` installer. It isn't in the dx bundler for production yet — and the Research Gallery
could become a strong desktop app.
```bash
bun run check:tauri # checks the desktop setup — needs no Rust tools
bun run tauri:build # builds the app — needs Rust, the Tauri CLI and webview libraries
```
</details>
<sub>WordPress and Tauri marks from [Simple Icons](https://simpleicons.org) (CC0); the trademarks belong to their owners.</sub>
> **Mapbox token:** the dev server bakes one in; built pages take `?tk=pk.…`, a pasted token, or a
> token endpoint. Or skip Mapbox entirely: `provider: "maplibre"` on any instance renders on MapLibre
> with no key. Every command and prerequisite:
> [documentation/README-SECTION-3-cli-commands.md](./documentation/README-SECTION-3-cli-commands.md).
---
## Build it with an agent swarm
Every plugin has **one agent** responsible for building it, working in its own git worktree. The
plugins live together in the **Plugin Studio** — one repo, one shared library — and **one shepherd**
makes sure every agent is doing its job well. Changes land on `main` through a **merge queue** (the
model behind GitHub's merge queue and GitLab's merge trains): one at a time, rebased and gated.
**Many AI agents can build many plugins at once without breaking each other's work.** Every plugin
has one agent building it, and one shepherd agent makes sure every agent does its job well. All the
plugins live in one project with one shared library, and changes are added to `main` — the one
official version — one at a time.
![The delegation model: the Plugin Studio at the centre, a ring of plugins around it, one agent per plugin outside that, and one shepherd over all the agents](documentation/readme-images/delegation-model.png)
![The delegation model: main at the centre guarded by a merge queue, inside the Plugin Studio; eight plugins around it; one agent per plugin; and one shepherd over all the agents](documentation/readme-images/delegation-model.png)
<details>
<summary><b>What problem does this solve? (in plain words)</b></summary>
All the maps here share **one project, one library and one build step**. That's what makes them
work together, and it's also the danger: if one agent's change breaks the shared library, **every
map breaks at once**. And when several agents work in the same project at the same time, they step on
each other — two agents edit the same file, one change quietly erases another, or half-finished work
lands in the version everyone else is using.
So the project works like a busy kitchen with one head chef:
1. **Each agent works in its own copy of the project** — its own counter, so nobody bumps anybody.
2. **Before adding its work, each agent catches up**: it pulls in everyone else's latest work and
fixes any clashes with its own. The agent fixes the clash, not the next person.
3. **Only one agent adds work at a time.** They wait in a line (the *merge queue*), the checks run,
and only then does the change go into `main`. `main` always works.
4. **A shepherd agent watches everyone**: plugins built in the studio, shared parts really shared,
and every change going through the line.
That's how many plugins get built at the same time, in one project, without the maps breaking.
</details>
<details>
<summary><b>The words, if you're new to git</b></summary>
| Word | In plain terms |
|---|---|
| **repo** | the project folder, with its full history |
| **main** | the one official version of the project that everyone builds from |
| **worktree** | a private copy of the project for one agent to work in |
| **merge conflict** | two changes touched the same lines; someone has to decide how to combine them |
| **rebase** | catching your work up with everyone else's latest before adding it |
| **merge queue** | the line agents wait in, so only one change goes into `main` at a time |
</details>
<details>
<summary><b>The commands (for the curious)</b></summary>
```bash
# one worktree + one agent per plugin
# one private copy (worktree) + one agent per plugin
git worktree add -b regions/work ../dx-maps-regions main
git worktree add -b upg100/work ../dx-maps-upg100 main
# one dev server per agent, each on its own port
# one preview server per agent, each on its own port
bun x vite --port 5301 --strictPort
# land through the queue: one at a time, rebased, gated
# add work through the line: one at a time, caught up first, checks must pass
flock /tmp/dx-maps-main.lock sh -c '
git fetch origin && git rebase origin/main &&
bun run gates:pre && git push origin HEAD:main'
```
One agent owns the full build (it wipes the shared output first); everyone else checks their own
bundle with `BUNDLE=<name> bun x vite build`. How to contribute, and the bar for shared code:
[documentation/CONTRIBUTING.md](./documentation/CONTRIBUTING.md).
Diagram source: [`delegation-model.svg`](./documentation/readme-images/src/delegation-model.svg).
One agent owns the full build (it rebuilds everyone's output); the others check just their own
plugin with `BUNDLE=<name> bun x vite build`. How to contribute, and the bar for shared code:
[documentation/CONTRIBUTING.md](./documentation/CONTRIBUTING.md). Diagram source:
[`delegation-model.svg`](./documentation/readme-images/src/delegation-model.svg).
</details>
---
## The Plugin Studio — ★ where you work
Every plugin in one place, sorted by capability. Pick any plugin to run it live, see what it is
composed of, and copy its embed code. Tell your agent which plugins to **group**, and it composes
them into a higher-capability plugin with props, events and a layout — that is how the Research
Gallery was built. *(The studio is being redesigned; this screenshot tracks it.)*
**Every plugin in one place, sorted by capability.** Pick any plugin to run it live, fold open the
plugins inside it, and copy its embed code. Tell your agent which plugins to **group**, and it builds
a higher-capability plugin from them — that's how the Research Gallery was built.
![Capability 5 — the developer studio (plugin-studio)](documentation/readme-images/cap5-plugin-studio.png)
### Get it running
You need **git**, **[Bun](https://bun.sh)** and **Node ≥ 18**.
You need **git**, **[Bun](https://bun.sh)** and **Node ≥ 18**, plus a free
**[Mapbox](https://account.mapbox.com) public token** (it starts with `pk.`) so the maps have a
background.
```bash
git clone https://git.nuxtbase.app/doxa-open/doxa-maps.git
cd doxa-maps
bun install --frozen-lockfile
cp .env.example .env # then paste your pk. token after VITE_MAPBOX_TOKEN=
bun run dev # → http://localhost:5173/ is the Plugin Studio
```
No build needed first — the built tree is committed. To start a plugin, copy
No build needed first — the built maps are already in the repo. To start a plugin, copy
`plugin-designs/template-bundle/` to `plugin-designs/<your-map>/`; the studio finds it automatically.
<details>
<summary><b>Make changes to the studio itself</b></summary>
The studio is a plugin like any other: `plugin-designs/plugin-studio/plugin-studio.vue`.
1. Run `bun run dev` and open http://localhost:5173/ — that page *is* the studio.
2. Edit `plugin-studio.vue` (or ask your agent to). The page updates as you save.
3. When it looks right, build just the studio and refresh its security hashes:
```bash
BUNDLE=plugin-studio bun x vite build
bun run fix:sri
```
4. Check it opens every plugin: `bun run smoke:studio` (with `bun run dev` running).
</details>
<details>
<summary><b>No Mapbox token? And other map settings</b></summary>
- **No token:** set `"provider": "maplibre"` on any plugin (or add `?provider=maplibre` to a map's
page) and it draws with MapLibre, which needs no key.
- **Built pages** take a token from `?tk=pk.…` in the address, from a token you paste once into the
page, or from a token endpoint.
- Every command and prerequisite, per deployment method:
[documentation/README-SECTION-3-cli-commands.md](./documentation/README-SECTION-3-cli-commands.md).
</details>
| Command | What it does |
|---|---|
| `bun run dev` | The Plugin Studio, hot-reloading, at http://localhost:5173 |
| `BUNDLE=<name> bun x vite build` | Build one bundle only |
| `BUILD_ALLOW=1 bun run build` | The full deployable tree in `public/doxa-maps/` (~4 min; refuses without the flag, to protect a shared tree) |
| `bun run serve` | Serve the built tree at http://localhost:4173 |
| `bun run check:settled` | Exit 0 = the built tree is safe to measure right now |
| `bun run gates:pre` · `bun run gates:post` | The verification gates |
| `bun run shots` | Regenerate these screenshots (`ONLY=<name>` for one) |
| `bun run dev` | The Plugin Studio, updating as you save, at http://localhost:5173 |
| `BUNDLE=<name> bun x vite build` | Build one plugin only |
| `BUILD_ALLOW=1 bun run build` | Build everything into `public/doxa-maps/` (~4 min; refuses without the flag, to protect a shared project) |
| `bun run serve` | Serve the built maps at http://localhost:4173 |
| `bun run gates:pre` · `bun run gates:post` | The checks that keep `main` working |
| `bun run shots` | Retake these screenshots (`ONLY=<name>` for one) |
---
@ -225,9 +431,9 @@ No build needed first — the built tree is committed. To start a plugin, copy
```text
plugin-designs/ YOUR maps — one folder per plugin, a free zone
library/ the SHARED toolkit (layouts, components, composables) — a high bar
public/doxa-maps/ the build output: the drop-anywhere tree + manifest.json
deployment/ Capacitor · WordPress · Tauri
internal/ the build generator and the gates
public/doxa-maps/ the build output: the drop-anywhere folder + manifest.json
deployment/ mobile (Capacitor) · WordPress · desktop (Tauri)
internal/ the build and its checks
documentation/ tutorial · how-to · reference · architecture — start at documentation/README.md
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 667 KiB

After

Width:  |  Height:  |  Size: 820 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 127 KiB

View file

@ -1,4 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1500" height="1826" viewBox="0 0 1500 1826" font-family="Inter, 'Helvetica Neue', Arial, sans-serif">
<svg xmlns="http://www.w3.org/2000/svg" width="1500" height="2030" viewBox="0 0 1500 2030" font-family="Inter, 'Helvetica Neue', Arial, sans-serif">
<defs>
<marker id="aA" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto"><path d="M0,0 L10,5 L0,10 z" fill="#2563eb"/></marker>
<marker id="aS" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M0,0 L10,5 L0,10 z" fill="#7c3aed"/></marker>
@ -9,7 +9,7 @@
<path id="agL" d="M 194,850 A 556,556 0 0 1 1306,850"/>
<path id="mqL" d="M 679,884 A 71,71 0 0 0 821,884"/>
</defs>
<rect width="1500" height="1826" fill="#ffffff"/>
<rect width="1500" height="2030" fill="#ffffff"/>
<text x="750" y="54" text-anchor="middle" font-size="34" font-weight="800" fill="#0f172a">How dx-maps is built: one studio, one agent per plugin, one shepherd</text>
<circle cx="750" cy="850" r="540" fill="none" stroke="#2563eb" stroke-width="1.6" stroke-dasharray="3 7" opacity=".75"/>
<circle cx="750" cy="850" r="355" fill="none" stroke="#15803d" stroke-width="2.2" opacity=".85"/>
@ -23,14 +23,24 @@
<line x1="193.8" y1="619.6" x2="210.5" y2="626.5" stroke="#7c3aed" stroke-width="2" opacity=".7" marker-end="url(#aS)"/>
<line x1="519.6" y1="293.8" x2="526.5" y2="310.5" stroke="#7c3aed" stroke-width="2" opacity=".7" marker-end="url(#aS)"/>
<circle cx="750" cy="850" r="190" fill="#fffbeb" stroke="#d97706" stroke-width="3.5"/>
<text font-size="24" font-weight="800" fill="#d97706" letter-spacing="4"><textPath href="#stL" startOffset="50%" text-anchor="middle">PLUGIN STUDIO</textPath></text>
<text x="750" y="770" text-anchor="middle" font-size="14" fill="#92400e">one repo · every plugin in one place</text>
<text x="750" y="790" text-anchor="middle" font-size="14" fill="#92400e">shared library/ (@map)</text>
<circle cx="750" cy="884" r="78" fill="#fef2f2" stroke="#dc2626" stroke-width="2" stroke-dasharray="7 5"/>
<circle cx="750" cy="884" r="44" fill="#166534"/>
<text x="750" y="890" text-anchor="middle" font-size="22" font-weight="800" fill="#fff">main</text>
<text x="750" y="906" text-anchor="middle" font-size="10.5" fill="#dcfce7">always green</text>
<text font-size="11" font-weight="700" fill="#b91c1c" letter-spacing="1.2"><textPath href="#mqL" startOffset="50%" text-anchor="middle">MERGE QUEUE · ONE AT A TIME</textPath></text>
<text font-size="22" font-weight="800" fill="#d97706" letter-spacing="4"><textPath href="#stL" startOffset="50%" text-anchor="middle">PLUGIN STUDIO · ONE REPO</textPath></text>
<path id="stB" d="M 584,850 A 166,166 0 0 0 916,850" fill="none"/>
<text font-size="14" font-weight="700" fill="#92400e" letter-spacing="1.5"><textPath href="#stB" startOffset="50%" text-anchor="middle">shared library/ (@map) · every plugin in one place</textPath></text>
<circle cx="750" cy="850" r="120" fill="#fef2f2" stroke="#dc2626" stroke-width="3" stroke-dasharray="10 6"/>
<path id="mqT" d="M 646,850 A 104,104 0 0 1 854,850" fill="none"/>
<path id="mqB" d="M 644,850 A 106,106 0 0 0 856,850" fill="none"/>
<text font-size="13" font-weight="800" fill="#b91c1c" letter-spacing="1.6"><textPath href="#mqT" startOffset="50%" text-anchor="middle">MERGE QUEUE · GUARD</textPath></text>
<text font-size="12" font-weight="700" fill="#b91c1c" letter-spacing="1.2"><textPath href="#mqB" startOffset="50%" text-anchor="middle">ONE AGENT AT A TIME</textPath></text>
<rect x="617" y="846" width="26" height="20" rx="4" fill="#dc2626"/>
<path d="M 622,846 v -6 a 8,8 0 0 1 16,0 v 6" fill="none" stroke="#dc2626" stroke-width="3.5"/>
<circle cx="630" cy="856" r="3" fill="#fff"/>
<rect x="857" y="846" width="26" height="20" rx="4" fill="#dc2626"/>
<path d="M 862,846 v -6 a 8,8 0 0 1 16,0 v 6" fill="none" stroke="#dc2626" stroke-width="3.5"/>
<circle cx="870" cy="856" r="3" fill="#fff"/>
<circle cx="750" cy="850" r="72" fill="#166534" stroke="#14532d" stroke-width="3"/>
<text x="750" y="852" text-anchor="middle" font-size="34" font-weight="800" fill="#fff">main</text>
<text x="750" y="876" text-anchor="middle" font-size="12.5" fill="#dcfce7">the one branch</text>
<text x="750" y="892" text-anchor="middle" font-size="12.5" fill="#dcfce7">always green</text>
<text font-size="16" font-weight="800" fill="#15803d" letter-spacing="3"><textPath href="#plL" startOffset="50%" text-anchor="middle">PLUGINS</textPath></text>
<text font-size="16" font-weight="800" fill="#2563eb" letter-spacing="3"><textPath href="#agL" startOffset="18%" text-anchor="middle">AGENTS</textPath></text>
<line x1="862.1" y1="579.3" x2="822.7" y2="674.5" stroke="#15803d" stroke-width="1.4" opacity=".45"/>
@ -112,48 +122,74 @@
<text x="834" y="192" font-size="17" fill="#4c1d95">one shepherd makes sure all plugin agents</text>
<text x="834" y="216" font-size="17" fill="#4c1d95">are doing their job building the plugins well</text>
<text x="750" y="1502" text-anchor="middle" font-size="20" font-weight="800" fill="#1e3a8a">each plugin has one agent responsible for building it</text>
<rect x="36" y="110" width="300" height="208" rx="14" fill="#f8fafc" stroke="#e2e8f0" stroke-width="2"/>
<circle cx="64" cy="144" r="12" fill="#fffbeb" stroke="#d97706" stroke-width="2.5"/>
<text x="88" y="149" font-size="15" fill="#334155">Plugin Studio — the one repo</text>
<circle cx="64" cy="180" r="12" fill="#f0fdf4" stroke="#15803d" stroke-width="2.5"/>
<text x="88" y="185" font-size="15" fill="#334155">plugin (badge = capability)</text>
<circle cx="64" cy="216" r="12" fill="#ecfeff" stroke="#0891b2" stroke-width="2.5"/>
<text x="88" y="221" font-size="15" fill="#334155">Experimental shelf</text>
<circle cx="64" cy="252" r="12" fill="#eff6ff" stroke="#2563eb" stroke-width="2.5"/>
<text x="88" y="257" font-size="15" fill="#334155">agent — builds one plugin</text>
<circle cx="64" cy="288" r="12" fill="#f5f3ff" stroke="#7c3aed" stroke-width="2.5"/>
<text x="88" y="293" font-size="15" fill="#334155">shepherd — oversees all</text>
<line x1="750" y1="956" x2="750" y2="1542" stroke="#334155" stroke-width="2.5" stroke-dasharray="2 5" opacity=".0"/>
<rect x="60" y="1536" width="1380" height="230" rx="20" fill="#f8fafc" stroke="#cbd5e1" stroke-width="2"/>
<text x="750" y="1580" text-anchor="middle" font-size="22" font-weight="800" fill="#0f172a">main → ONE build → public/doxa-maps/ → five ways to ship</text>
<rect x="80.0" y="1608" width="236" height="130" rx="14" fill="#fff" stroke="#15803d" stroke-width="2.5"/>
<text x="198.0" y="1630" text-anchor="middle" font-size="13" font-weight="800" fill="#64748b">METHOD 1</text>
<text x="198.0" y="1662" text-anchor="middle" font-size="21" font-weight="800" fill="#0f172a">iframe</text>
<text x="198.0" y="1688" text-anchor="middle" font-size="15" fill="#475569">any website</text>
<rect x="120.0" y="1702" width="156" height="26" rx="13" fill="#15803d" opacity=".13"/>
<text x="198.0" y="1720" text-anchor="middle" font-size="13" font-weight="800" fill="#15803d">PRODUCTION</text>
<rect x="356.0" y="1608" width="236" height="130" rx="14" fill="#fff" stroke="#15803d" stroke-width="2.5"/>
<text x="474.0" y="1630" text-anchor="middle" font-size="13" font-weight="800" fill="#64748b">METHOD 2</text>
<text x="474.0" y="1662" text-anchor="middle" font-size="21" font-weight="800" fill="#0f172a">Mobile app</text>
<text x="474.0" y="1688" text-anchor="middle" font-size="15" fill="#475569">Capacitor</text>
<rect x="396.0" y="1702" width="156" height="26" rx="13" fill="#15803d" opacity=".13"/>
<text x="474.0" y="1720" text-anchor="middle" font-size="13" font-weight="800" fill="#15803d">BUILT</text>
<rect x="632.0" y="1608" width="236" height="130" rx="14" fill="#fff" stroke="#15803d" stroke-width="2.5"/>
<text x="750.0" y="1630" text-anchor="middle" font-size="13" font-weight="800" fill="#64748b">METHOD 3</text>
<text x="750.0" y="1662" text-anchor="middle" font-size="21" font-weight="800" fill="#0f172a">JavaScript embed</text>
<text x="750.0" y="1688" text-anchor="middle" font-size="15" fill="#475569">script + SRI</text>
<rect x="672.0" y="1702" width="156" height="26" rx="13" fill="#15803d" opacity=".13"/>
<text x="750.0" y="1720" text-anchor="middle" font-size="13" font-weight="800" fill="#15803d">PRODUCTION</text>
<rect x="908.0" y="1608" width="236" height="130" rx="14" fill="#fff" stroke="#d97706" stroke-width="2.5"/>
<text x="1026.0" y="1630" text-anchor="middle" font-size="13" font-weight="800" fill="#64748b">METHOD 4</text>
<text x="1026.0" y="1662" text-anchor="middle" font-size="21" font-weight="800" fill="#0f172a">WordPress</text>
<text x="1026.0" y="1688" text-anchor="middle" font-size="15" fill="#475569">Disciple Tools</text>
<rect x="948.0" y="1702" width="156" height="26" rx="13" fill="#d97706" opacity=".13"/>
<text x="1026.0" y="1720" text-anchor="middle" font-size="13" font-weight="800" fill="#d97706">PROOF OF CONCEPT</text>
<rect x="1184.0" y="1608" width="236" height="130" rx="14" fill="#fff" stroke="#d97706" stroke-width="2.5"/>
<text x="1302.0" y="1630" text-anchor="middle" font-size="13" font-weight="800" fill="#64748b">METHOD 5</text>
<text x="1302.0" y="1662" text-anchor="middle" font-size="21" font-weight="800" fill="#0f172a">Desktop</text>
<text x="1302.0" y="1688" text-anchor="middle" font-size="15" fill="#475569">Tauri</text>
<rect x="1224.0" y="1702" width="156" height="26" rx="13" fill="#d97706" opacity=".13"/>
<text x="1302.0" y="1720" text-anchor="middle" font-size="13" font-weight="800" fill="#d97706">PROOF OF CONCEPT</text>
<rect x="36" y="110" width="300" height="280" rx="14" fill="#f8fafc" stroke="#e2e8f0" stroke-width="2"/>
<circle cx="64" cy="144" r="12" fill="#166534" stroke="#14532d" stroke-width="2.5"/>
<text x="88" y="149" font-size="15" fill="#334155">main — the one branch</text>
<circle cx="64" cy="180" r="12" fill="#fef2f2" stroke="#dc2626" stroke-width="2.5"/>
<text x="88" y="185" font-size="15" fill="#334155">merge queue — one at a time</text>
<circle cx="64" cy="216" r="12" fill="#fffbeb" stroke="#d97706" stroke-width="2.5"/>
<text x="88" y="221" font-size="15" fill="#334155">Plugin Studio — the one repo</text>
<circle cx="64" cy="252" r="12" fill="#f0fdf4" stroke="#15803d" stroke-width="2.5"/>
<text x="88" y="257" font-size="15" fill="#334155">plugin (badge = capability)</text>
<circle cx="64" cy="288" r="12" fill="#ecfeff" stroke="#0891b2" stroke-width="2.5"/>
<text x="88" y="293" font-size="15" fill="#334155">Experimental shelf</text>
<circle cx="64" cy="324" r="12" fill="#eff6ff" stroke="#2563eb" stroke-width="2.5"/>
<text x="88" y="329" font-size="15" fill="#334155">agent — builds one plugin</text>
<circle cx="64" cy="360" r="12" fill="#f5f3ff" stroke="#7c3aed" stroke-width="2.5"/>
<text x="88" y="365" font-size="15" fill="#334155">shepherd — oversees all</text>
<rect x="60" y="1536" width="1380" height="218" rx="20" fill="#fef2f2" stroke="#fca5a5" stroke-width="2"/>
<text x="750" y="1578" text-anchor="middle" font-size="22" font-weight="800" fill="#7f1d1d">How main is protected — so many plugins can be built at once</text>
<rect x="80.0" y="1602" width="410" height="130" rx="14" fill="#fff" stroke="#dc2626" stroke-width="2"/>
<circle cx="116.0" cy="1638" r="20" fill="#dc2626"/>
<text x="116.0" y="1645" text-anchor="middle" font-size="20" font-weight="800" fill="#fff">1</text>
<text x="148.0" y="1645" font-size="20" font-weight="800" fill="#0f172a">Work in your own copy</text>
<text x="102.0" y="1686" font-size="16" fill="#475569">each agent builds its own plugin,</text>
<text x="102.0" y="1710" font-size="16" fill="#475569">in its own private copy of the project</text>
<text x="517.5" y="1677" text-anchor="middle" font-size="30" font-weight="800" fill="#dc2626">→</text>
<rect x="545.0" y="1602" width="410" height="130" rx="14" fill="#fff" stroke="#dc2626" stroke-width="2"/>
<circle cx="581.0" cy="1638" r="20" fill="#dc2626"/>
<text x="581.0" y="1645" text-anchor="middle" font-size="20" font-weight="800" fill="#fff">2</text>
<text x="613.0" y="1645" font-size="20" font-weight="800" fill="#0f172a">Catch up, fix the clashes</text>
<text x="567.0" y="1686" font-size="16" fill="#475569">pull in everyone else's latest work and</text>
<text x="567.0" y="1710" font-size="16" fill="#475569">fix any clashes before adding yours</text>
<text x="982.5" y="1677" text-anchor="middle" font-size="30" font-weight="800" fill="#dc2626">→</text>
<rect x="1010.0" y="1602" width="410" height="130" rx="14" fill="#fff" stroke="#dc2626" stroke-width="2"/>
<circle cx="1046.0" cy="1638" r="20" fill="#dc2626"/>
<text x="1046.0" y="1645" text-anchor="middle" font-size="20" font-weight="800" fill="#fff">3</text>
<text x="1078.0" y="1645" font-size="20" font-weight="800" fill="#0f172a">Add changes one at a time</text>
<text x="1032.0" y="1686" font-size="16" fill="#475569">the queue lets ONE agent in at a time;</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"/>
<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>
<rect x="80.0" y="1852" width="236" 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="198.0" y="1906" text-anchor="middle" font-size="21" font-weight="800" fill="#0f172a">iframe</text>
<text x="198.0" y="1932" text-anchor="middle" font-size="15" fill="#475569">any website</text>
<rect x="90.0" y="1946" width="216" 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>
<rect x="356.0" y="1852" width="236" 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="474.0" y="1906" text-anchor="middle" font-size="21" font-weight="800" fill="#0f172a">Mobile app</text>
<text x="474.0" y="1932" text-anchor="middle" font-size="15" fill="#475569">Capacitor</text>
<rect x="366.0" y="1946" width="216" 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>
<rect x="632.0" y="1852" width="236" height="130" rx="14" fill="#fff" stroke="#15803d" 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="750.0" y="1906" text-anchor="middle" font-size="21" font-weight="800" fill="#0f172a">JavaScript embed</text>
<text x="750.0" y="1932" text-anchor="middle" font-size="15" fill="#475569">script + SRI</text>
<rect x="642.0" y="1946" width="216" height="26" rx="13" fill="#15803d" opacity=".13"/>
<text x="750.0" y="1964" text-anchor="middle" font-size="11.5" font-weight="800" fill="#15803d">PRODUCTION</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>

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -3,9 +3,10 @@
<rect x="1.5" y="1.5" width="1197" height="627" rx="27" fill="none" stroke="#e2e8f0" stroke-width="3"/>
<g transform="translate(110,175) scale(11.5)"><path d="M21.469 6.825c.84 1.537 1.318 3.3 1.318 5.175 0 3.979-2.156 7.456-5.363 9.325l3.295-9.527c.615-1.54.82-2.771.82-3.864 0-.405-.026-.78-.07-1.11m-7.981.105c.647-.03 1.232-.105 1.232-.105.582-.075.514-.93-.067-.899 0 0-1.755.135-2.88.135-1.064 0-2.85-.15-2.85-.15-.585-.03-.661.855-.075.885 0 0 .54.061 1.125.09l1.68 4.605-2.37 7.08L5.354 6.9c.649-.03 1.234-.1 1.234-.1.585-.075.516-.93-.065-.896 0 0-1.746.138-2.874.138-.2 0-.438-.008-.69-.015C4.911 3.15 8.235 1.215 12 1.215c2.809 0 5.365 1.072 7.286 2.833-.046-.003-.091-.009-.141-.009-1.06 0-1.812.923-1.812 1.914 0 .89.513 1.643 1.06 2.531.411.72.89 1.643.89 2.977 0 .915-.354 1.994-.821 3.479l-1.075 3.585-3.9-11.61.001.014zM12 22.784c-1.059 0-2.081-.153-3.048-.437l3.237-9.406 3.315 9.087c.024.053.05.101.078.149-1.12.393-2.325.609-3.582.609M1.211 12c0-1.564.336-3.05.935-4.39L7.29 21.709C3.694 19.96 1.212 16.271 1.211 12M12 0C5.385 0 0 5.385 0 12s5.385 12 12 12 12-5.385 12-12S18.615 0 12 0" fill="#21759B"/></g>
<text x="480" y="250" font-size="44" font-weight="800" fill="#0f172a">WordPress / Disciple Tools</text>
<text x="480" y="315" font-size="27" fill="#475569">A plugin for WordPress sites, including Disciple Tools:</text>
<text x="480" y="355" font-size="27" fill="#475569">a [doxa_map] shortcode and a block editor block.</text>
<rect x="480" y="400" width="360" height="62" rx="31" fill="#fef3c7" stroke="#d97706" stroke-width="2.5"/>
<text x="660" y="441" text-anchor="middle" font-size="26" font-weight="800" fill="#92400e">PROOF OF CONCEPT</text>
<text x="480" y="510" font-size="21" fill="#64748b">Contract-tested and working — not yet production-ready.</text>
<text x="480" y="315" font-size="27" fill="#475569">We got a JAMstack map running inside</text>
<text x="480" y="355" font-size="27" fill="#475569">WordPress and Disciple Tools.</text>
<rect x="480" y="400" width="420" height="62" rx="31" fill="#fef3c7" stroke="#d97706" stroke-width="2.5"/>
<text x="690" y="441" text-anchor="middle" font-size="26" font-weight="800" fill="#92400e">PROVEN · NOT YET LIVE</text>
<text x="480" y="508" font-size="22" fill="#64748b">Proven to work — not yet a production</text>
<text x="480" y="538" font-size="22" fill="#64748b">feature of the map bundler.</text>
</svg>

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -3,9 +3,10 @@
<rect x="1.5" y="1.5" width="1197" height="627" rx="27" fill="none" stroke="#e2e8f0" stroke-width="3"/>
<g transform="translate(110,175) scale(11.5)"><path d="M13.912 0a8.72 8.72 0 0 0-8.308 6.139c1.05-.515 2.18-.845 3.342-.976 2.415-3.363 7.4-3.412 9.88-.097 2.48 3.315 1.025 8.084-2.883 9.45a6.131 6.131 0 0 1-.3 2.762 8.72 8.72 0 0 0 3.01-1.225A8.72 8.72 0 0 0 13.913 0zm.082 6.451a2.284 2.284 0 1 0-.15 4.566 2.284 2.284 0 0 0 .15-4.566zm-5.629.27a8.72 8.72 0 0 0-3.031 1.235 8.72 8.72 0 1 0 13.06 9.9131 10.173 10.174 0 0 1-3.343.965 6.125 6.125 0 1 1-7.028-9.343 6.114 6.115 0 0 1 .342-2.772zm1.713 6.27a2.284 2.284 0 0 0-2.284 2.283 2.284 2.284 0 0 0 2.284 2.284 2.284 2.284 0 0 0 2.284-2.284 2.284 2.284 0 0 0-2.284-2.284z" fill="#24C8DB"/></g>
<text x="480" y="250" font-size="54" font-weight="800" fill="#0f172a">Desktop app (Tauri)</text>
<text x="480" y="315" font-size="27" fill="#475569">The same map tree wrapped as a desktop app:</text>
<text x="480" y="355" font-size="27" fill="#475569">a native binary and a .deb installer.</text>
<rect x="480" y="400" width="360" height="62" rx="31" fill="#fef3c7" stroke="#d97706" stroke-width="2.5"/>
<text x="660" y="441" text-anchor="middle" font-size="26" font-weight="800" fill="#92400e">PROOF OF CONCEPT</text>
<text x="480" y="510" font-size="21" fill="#64748b">Contract-tested and working — not yet production-ready.</text>
<text x="480" y="315" font-size="27" fill="#475569">We got the JAMstack map app running</text>
<text x="480" y="355" font-size="27" fill="#475569">as a desktop app.</text>
<rect x="480" y="400" width="420" height="62" rx="31" fill="#fef3c7" stroke="#d97706" stroke-width="2.5"/>
<text x="690" y="441" text-anchor="middle" font-size="26" font-weight="800" fill="#92400e">PROVEN · NOT YET LIVE</text>
<text x="480" y="508" font-size="22" fill="#64748b">Proven, not yet in production. The Research</text>
<text x="480" y="538" font-size="22" fill="#64748b">Gallery could become a strong desktop app.</text>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB