updates 33.1 139

This commit is contained in:
2026-08-10 09:19:19 -03:00
parent 9a6337e493
commit 910927993e
49 changed files with 1876 additions and 341 deletions

5
.gitignore vendored
View File

@@ -9,6 +9,11 @@ __pycache__/
.venv/
venv/
# Secrets. There was no rule here, which is how a real API key ended up tracked
# in station/tools/tester/.env. Templates still ship.
.env
!.env.example
# Node
node_modules/

View File

@@ -221,8 +221,27 @@ framework, and never something a test reaches into.
## Known Broken (found, not yet fixed)
- `tester/tests/_dev/test_health.py` imports `..endpoints`, which does not exist in
core — the module is unimportable.
- Client leaks remain in `station/monitors/databrowse/` (README + `index.html`) and
`atlas/main.py` (`PAWPRINT_URL`, role labels). Core should carry no client
vocabulary; it belongs in `cfg/<room>/`.
- `tester/tests/example/test_health.py` imports `pytest`, which is neither a
dependency nor allowed here — `tests/README.md` says stdlib `unittest` and
`httpx`, no pytest. It fails to import, so `python -m tester discover` reports
it as `_FailedTest`. It also duplicates `test_template.py`, which is the
sanctioned example. Removing it is probably the fix; core ships no tests.
- Old vocabulary survives in prose — `album`, `larder`, `ward`, `nest` and
`pawprint` still appear in READMEs, `docs/`, and this file's history. Live code
is clean; the docs lag.
## Fixed
- **A real API key was tracked** in `station/tools/tester/.env`. Untracked, `.env`
added to `.gitignore`, `.env.example` added. **The key is still in git history
and must be rotated** — untracking does not unpublish it.
- Client vocabulary is out of live code. `atlas/main.py` was written entirely as
`Album`/`larder`/`PAWPRINT_URL`; `databrowse` docs described a `larder/` the
code stopped using; `get-api-key.sh` defaulted to the client's database name.
- Two dead back-links, found while renaming: `atlas/main.py` passed
`pawprint_url` and `artery/index.html` read `pawprint_url`, while `run.py`
passes `soleprint_url`. Neither "← Soleprint" link had ever rendered.
- `atlas/main.py` fetched `/api/data/album`; `main.py` serves `/api/data/atlas`.
`get_data()` had been failing and returning empty lists.
- `tester/tests/_dev/test_health.py` imported a non-existent `..endpoints`. The
import was unused; removing it makes the module discoverable (2 tests).

View File

@@ -1,5 +1,5 @@
"""
Pawprint Data Layer
Soleprint Data Layer
JSON file storage (future: MongoDB)
"""

View File

@@ -1,5 +1,5 @@
"""
Pawprint Data Layer
Soleprint Data Layer
JSON file storage (future: MongoDB)
"""

View File

@@ -1,5 +1,5 @@
{
"items": [
{"name": "pawprint-local", "slug": "pawprint-local", "title": "Pawprint Local", "status": "dev", "config_path": "deploy/pawprint-local"}
{"name": "standalone-local", "slug": "standalone-local", "title": "Standalone Local", "status": "dev", "config_path": "deploy/standalone-local"}
]
}

67
docs/graphs/README.md Normal file
View File

@@ -0,0 +1,67 @@
# Graphs
The `.dot` files carry **structure and meaning**. The palette lives in
`themes/*.gvpr` and is applied at render time.
```bash
./render.sh # every graph, every theme
./render.sh lucid # one theme
./render.sh dark system_overview
```
Needs graphviz (`sudo apt install graphviz`). The committed SVGs work without
it; this is only needed to re-render.
## Why themes, and why gvpr
The sources used to hardcode the dark palette inline, which meant one look and
no way to get another. `gvpr` rewrites the *parsed* graph, so it overrides
whatever a `.dot` set — one source renders in any theme without being edited.
Command-line `-G`/`-N`/`-E` flags would not do: those are defaults, and an
attribute written in the file beats them.
Colour is **baked into each SVG** rather than driven by CSS, because both docs
sites embed graphs with `<img src=...>`. That makes the SVG a separate document
the page's stylesheet cannot reach.
## Output
| Theme | Writes | For |
| --- | --- | --- |
| `dark` | `<name>.svg` | the docs site — this is the default, and what `docs/data/en/*.md` links to |
| `lucid` | `<name>.lucid.svg` | regulated documents, print, and sitting beside a real lucid.app export |
## Classes
Nodes, edges, clusters and the graph itself may carry a `class`. Anything
untagged gets the theme's neutral treatment.
| Class | Means | dark | lucid |
| --- | --- | --- | --- |
| `accent` | the emphasised thing | amber outline | blue outline, pale blue fill |
| `accent-text` | emphasised *label*, not a box | amber text | blue text |
| `ok` | live, working | green text | green outline, pale green fill |
| `artery` / `atlas` / `station` | belongs to that system | that system's colour | its pale equivalent |
| `muted` | present but not the point | grey text | grey fill |
`class` survives into the SVG (`<g class="node accent">`), so an **inlined** SVG
can also be styled by page CSS. That is not how the docs embed them, but it is
there if a page wants it.
## What themes never touch
- **`shape`** — a cylinder is a datastore, not a decoration.
- **`style=invis`** — layout scaffolding. Filling it would draw it.
- **`style=dashed`** — a weaker relationship; the theme preserves it and adds to it.
- **`label`, `rankdir`, `rank`, `fontsize`** — content and layout.
## Adding a theme
Drop a `themes/<name>.gvpr` in beside the others; `render.sh` picks it up with no
edit. Match the palette to `soleprint/common/theme/themes/<name>.css` so a
diagram and the page around it are the same visual language.
Name fonts that exist on the target. `lucid.gvpr` uses Arial deliberately: it is
on every Windows box and fontconfig aliases it to Liberation Sans on Linux, so
the SVG measures the same on both and text does not reflow out of its box.

View File

@@ -1,43 +1,37 @@
digraph artery_hierarchy {
bgcolor="#0a0a0a"
rankdir=LR
fontname="Helvetica"
node [fontname="Helvetica" fontsize=11 style=filled color="#333" fontcolor="#e5e5e5" shape=box]
edge [fontname="Helvetica" fontsize=9 fontcolor="#a3a3a3" color="#b91c1c"]
node [fontname="Helvetica" fontsize=11 style=filled shape=box]
edge [class="artery" fontname="Helvetica" fontsize=9]
label="Artery — Component Hierarchy"
labelloc=t
fontsize=14
fontcolor="#fca5a5"
vein [label="Vein\nstateless API connector" fillcolor="#1a1a1a"]
pulse [label="Pulse\nVein + Room + Depot" fillcolor="#1a1a1a"]
plexus [label="Plexus\nfull app: backend\n+ frontend + DB" fillcolor="#1a1a1a"]
shunt [label="Shunt\nfake connector\nfor testing" fillcolor="#1a1a1a" color="#d4a574"]
vein [label="Vein\nstateless API connector"]
pulse [label="Pulse\nVein + Room + Depot"]
plexus [label="Plexus\nfull app: backend\n+ frontend + DB"]
shunt [class="accent" label="Shunt\nfake connector\nfor testing"]
vein -> pulse [label="compose"]
pulse -> plexus [label="extend"]
shunt -> vein [label="replaces" style=dashed color="#d4a574" fontcolor="#d4a574"]
shunt -> vein [class="accent" label="replaces" style=dashed]
// Examples
subgraph cluster_examples {
label="Live Veins"
style=dashed
color="#333"
fontcolor="#666"
jira [label="Jira" fillcolor="#1a1a1a" fontcolor="#15803d" fontsize=9]
google [label="Google" fillcolor="#1a1a1a" fontcolor="#d4a574" fontsize=9]
ia [label="IA" fillcolor="#1a1a1a" fontcolor="#15803d" fontsize=9]
jira [class="ok" label="Jira" fontsize=9]
google [class="accent-text" label="Google" fontsize=9]
ia [class="ok" label="IA" fontsize=9]
}
subgraph cluster_shunts {
label="Shunts"
style=dashed
color="#333"
fontcolor="#666"
mp [label="MercadoPago" fillcolor="#1a1a1a" fontcolor="#d4a574" fontsize=9]
mp [class="accent-text" label="MercadoPago" fontsize=9]
}
jira -> vein [style=invis]

View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 14.1.2 (0)
-->
<!-- Title: artery_hierarchy Pages: 1 -->
<svg width="776pt" height="292pt"
viewBox="0.00 0.00 776.00 292.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 287.75)">
<title>artery_hierarchy</title>
<polygon fill="#ffffff" stroke="none" points="-4,4 -4,-287.75 772.25,-287.75 772.25,4 -4,4"/>
<text xml:space="preserve" text-anchor="middle" x="384.12" y="-266.45" font-family="Arial" font-size="14.00" fill="#1f2933">Artery — Component Hierarchy</text>
<g id="clust1" class="cluster">
<title>cluster_examples</title>
<path fill="#f5f7fa" stroke="#cbd2d9" stroke-dasharray="5,2" d="M143.75,-68C143.75,-68 197.25,-68 197.25,-68 203.25,-68 209.25,-74 209.25,-80 209.25,-80 209.25,-240 209.25,-240 209.25,-246 203.25,-252 197.25,-252 197.25,-252 143.75,-252 143.75,-252 137.75,-252 131.75,-246 131.75,-240 131.75,-240 131.75,-80 131.75,-80 131.75,-74 137.75,-68 143.75,-68"/>
<text xml:space="preserve" text-anchor="middle" x="170.5" y="-234.7" font-family="Arial" font-size="14.00" fill="#616e7c">Live Veins</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_shunts</title>
<path fill="#f5f7fa" stroke="#cbd2d9" stroke-dasharray="5,2" d="M20,-8C20,-8 85.75,-8 85.75,-8 91.75,-8 97.75,-14 97.75,-20 97.75,-20 97.75,-72 97.75,-72 97.75,-78 91.75,-84 85.75,-84 85.75,-84 20,-84 20,-84 14,-84 8,-78 8,-72 8,-72 8,-20 8,-20 8,-14 14,-8 20,-8"/>
<text xml:space="preserve" text-anchor="middle" x="52.88" y="-66.7" font-family="Arial" font-size="14.00" fill="#616e7c">Shunts</text>
</g>
<!-- jira -->
<g id="node1" class="node ok">
<title>jira</title>
<path fill="#e6f5ec" stroke="#1a7f45" d="M185,-112C185,-112 155,-112 155,-112 149,-112 143,-106 143,-100 143,-100 143,-88 143,-88 143,-82 149,-76 155,-76 155,-76 185,-76 185,-76 191,-76 197,-82 197,-88 197,-88 197,-100 197,-100 197,-106 191,-112 185,-112"/>
<text xml:space="preserve" text-anchor="middle" x="170" y="-90.7" font-family="Arial" font-size="9.00" fill="#1f2933">Jira</text>
</g>
<!-- vein -->
<g id="node5" class="node">
<title>vein</title>
<path fill="#ffffff" stroke="#9aa5b1" d="M401,-82C401,-82 296.5,-82 296.5,-82 290.5,-82 284.5,-76 284.5,-70 284.5,-70 284.5,-58 284.5,-58 284.5,-52 290.5,-46 296.5,-46 296.5,-46 401,-46 401,-46 407,-46 413,-52 413,-58 413,-58 413,-70 413,-70 413,-76 407,-82 401,-82"/>
<text xml:space="preserve" text-anchor="middle" x="348.75" y="-67.05" font-family="Arial" font-size="11.00" fill="#1f2933">Vein</text>
<text xml:space="preserve" text-anchor="middle" x="348.75" y="-53.55" font-family="Arial" font-size="11.00" fill="#1f2933">stateless API connector</text>
</g>
<!-- jira&#45;&gt;vein -->
<!-- google -->
<g id="node2" class="node accent&#45;text">
<title>google</title>
<path fill="#ffffff" stroke="#9aa5b1" d="M185,-166C185,-166 155,-166 155,-166 149,-166 143,-160 143,-154 143,-154 143,-142 143,-142 143,-136 149,-130 155,-130 155,-130 185,-130 185,-130 191,-130 197,-136 197,-142 197,-142 197,-154 197,-154 197,-160 191,-166 185,-166"/>
<text xml:space="preserve" text-anchor="middle" x="170" y="-144.7" font-family="Arial" font-size="9.00" fill="#3a7dff">Google</text>
</g>
<!-- ia -->
<g id="node3" class="node ok">
<title>ia</title>
<path fill="#e6f5ec" stroke="#1a7f45" d="M185,-220C185,-220 155,-220 155,-220 149,-220 143,-214 143,-208 143,-208 143,-196 143,-196 143,-190 149,-184 155,-184 155,-184 185,-184 185,-184 191,-184 197,-190 197,-196 197,-196 197,-208 197,-208 197,-214 191,-220 185,-220"/>
<text xml:space="preserve" text-anchor="middle" x="170" y="-198.7" font-family="Arial" font-size="9.00" fill="#1f2933">IA</text>
</g>
<!-- mp -->
<g id="node4" class="node accent&#45;text">
<title>mp</title>
<path fill="#ffffff" stroke="#9aa5b1" d="M77.75,-52C77.75,-52 28,-52 28,-52 22,-52 16,-46 16,-40 16,-40 16,-28 16,-28 16,-22 22,-16 28,-16 28,-16 77.75,-16 77.75,-16 83.75,-16 89.75,-22 89.75,-28 89.75,-28 89.75,-40 89.75,-40 89.75,-46 83.75,-52 77.75,-52"/>
<text xml:space="preserve" text-anchor="middle" x="52.88" y="-30.7" font-family="Arial" font-size="9.00" fill="#3a7dff">MercadoPago</text>
</g>
<!-- shunt -->
<g id="node8" class="node accent">
<title>shunt</title>
<path fill="#d6e4ff" stroke="#3a7dff" d="M201.25,-58.25C201.25,-58.25 138.75,-58.25 138.75,-58.25 132.75,-58.25 126.75,-52.25 126.75,-46.25 126.75,-46.25 126.75,-21.75 126.75,-21.75 126.75,-15.75 132.75,-9.75 138.75,-9.75 138.75,-9.75 201.25,-9.75 201.25,-9.75 207.25,-9.75 213.25,-15.75 213.25,-21.75 213.25,-21.75 213.25,-46.25 213.25,-46.25 213.25,-52.25 207.25,-58.25 201.25,-58.25"/>
<text xml:space="preserve" text-anchor="middle" x="170" y="-43.8" font-family="Arial" font-size="11.00" fill="#1f2933">Shunt</text>
<text xml:space="preserve" text-anchor="middle" x="170" y="-30.3" font-family="Arial" font-size="11.00" fill="#1f2933">fake connector</text>
<text xml:space="preserve" text-anchor="middle" x="170" y="-16.8" font-family="Arial" font-size="11.00" fill="#1f2933">for testing</text>
</g>
<!-- mp&#45;&gt;shunt -->
<!-- pulse -->
<g id="node6" class="node">
<title>pulse</title>
<path fill="#ffffff" stroke="#9aa5b1" d="M595.5,-82C595.5,-82 498.5,-82 498.5,-82 492.5,-82 486.5,-76 486.5,-70 486.5,-70 486.5,-58 486.5,-58 486.5,-52 492.5,-46 498.5,-46 498.5,-46 595.5,-46 595.5,-46 601.5,-46 607.5,-52 607.5,-58 607.5,-58 607.5,-70 607.5,-70 607.5,-76 601.5,-82 595.5,-82"/>
<text xml:space="preserve" text-anchor="middle" x="547" y="-67.05" font-family="Arial" font-size="11.00" fill="#1f2933">Pulse</text>
<text xml:space="preserve" text-anchor="middle" x="547" y="-53.55" font-family="Arial" font-size="11.00" fill="#1f2933">Vein + Room + Depot</text>
</g>
<!-- vein&#45;&gt;pulse -->
<g id="edge1" class="edge artery">
<title>vein&#45;&gt;pulse</title>
<path fill="none" stroke="#c0392b" d="M413.47,-64C434.03,-64 456.89,-64 477.76,-64"/>
<polygon fill="#c0392b" stroke="#c0392b" points="477.75,-66.45 484.75,-64 477.75,-61.55 477.75,-66.45"/>
<text xml:space="preserve" text-anchor="middle" x="449.75" y="-65.95" font-family="Arial" font-size="9.00" fill="#c0392b">compose</text>
</g>
<!-- plexus -->
<g id="node7" class="node">
<title>plexus</title>
<path fill="#ffffff" stroke="#9aa5b1" d="M756.25,-88.25C756.25,-88.25 683.25,-88.25 683.25,-88.25 677.25,-88.25 671.25,-82.25 671.25,-76.25 671.25,-76.25 671.25,-51.75 671.25,-51.75 671.25,-45.75 677.25,-39.75 683.25,-39.75 683.25,-39.75 756.25,-39.75 756.25,-39.75 762.25,-39.75 768.25,-45.75 768.25,-51.75 768.25,-51.75 768.25,-76.25 768.25,-76.25 768.25,-82.25 762.25,-88.25 756.25,-88.25"/>
<text xml:space="preserve" text-anchor="middle" x="719.75" y="-73.8" font-family="Arial" font-size="11.00" fill="#1f2933">Plexus</text>
<text xml:space="preserve" text-anchor="middle" x="719.75" y="-60.3" font-family="Arial" font-size="11.00" fill="#1f2933">full app: backend</text>
<text xml:space="preserve" text-anchor="middle" x="719.75" y="-46.8" font-family="Arial" font-size="11.00" fill="#1f2933">+ frontend + DB</text>
</g>
<!-- pulse&#45;&gt;plexus -->
<g id="edge2" class="edge artery">
<title>pulse&#45;&gt;plexus</title>
<path fill="none" stroke="#c0392b" d="M607.72,-64C625.49,-64 644.89,-64 662.44,-64"/>
<polygon fill="#c0392b" stroke="#c0392b" points="662.37,-66.45 669.37,-64 662.37,-61.55 662.37,-66.45"/>
<text xml:space="preserve" text-anchor="middle" x="639.38" y="-65.95" font-family="Arial" font-size="9.00" fill="#c0392b">extend</text>
</g>
<!-- shunt&#45;&gt;vein -->
<g id="edge3" class="edge accent">
<title>shunt&#45;&gt;vein</title>
<path fill="none" stroke="#3a7dff" stroke-dasharray="5,2" d="M213.34,-41.19C232.09,-44.37 254.68,-48.2 275.94,-51.81"/>
<polygon fill="#3a7dff" stroke="#3a7dff" points="275.53,-54.23 282.84,-52.98 276.35,-49.4 275.53,-54.23"/>
<text xml:space="preserve" text-anchor="middle" x="248.88" y="-51.85" font-family="Arial" font-size="9.00" fill="#3a7dff">replaces</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@@ -4,98 +4,98 @@
<!-- Generated by graphviz version 14.1.2 (0)
-->
<!-- Title: artery_hierarchy Pages: 1 -->
<svg width="845pt" height="317pt"
viewBox="0.00 0.00 845.00 317.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 313.25)">
<svg width="845pt" height="294pt"
viewBox="0.00 0.00 845.00 294.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 290.25)">
<title>artery_hierarchy</title>
<polygon fill="#0a0a0a" stroke="none" points="-4,4 -4,-313.25 840.5,-313.25 840.5,4 -4,4"/>
<text xml:space="preserve" text-anchor="middle" x="418.25" y="-291.95" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#fca5a5">Artery — Component Hierarchy</text>
<polygon fill="#0a0a0a" stroke="none" points="-4,4 -4,-290.25 840.5,-290.25 840.5,4 -4,4"/>
<text xml:space="preserve" text-anchor="middle" x="418.25" y="-268.95" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d4a574">Artery — Component Hierarchy</text>
<g id="clust1" class="cluster">
<title>cluster_examples</title>
<polygon fill="#0a0a0a" stroke="#333333" stroke-dasharray="5,2" points="135.88,-8 135.88,-193 220.88,-193 220.88,-8 135.88,-8"/>
<text xml:space="preserve" text-anchor="middle" x="178.38" y="-175.7" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#666666">Live Veins</text>
<polygon fill="#0a0a0a" stroke="#333333" stroke-dasharray="5,2" points="135.88,-68 135.88,-253 220.88,-253 220.88,-68 135.88,-68"/>
<text xml:space="preserve" text-anchor="middle" x="178.38" y="-235.7" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#666666">Live Veins</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_shunts</title>
<polygon fill="#0a0a0a" stroke="#333333" stroke-dasharray="5,2" points="8,-199 8,-276 100,-276 100,-199 8,-199"/>
<text xml:space="preserve" text-anchor="middle" x="54" y="-258.7" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#666666">Shunts</text>
<polygon fill="#0a0a0a" stroke="#333333" stroke-dasharray="5,2" points="8,-8 8,-85 100,-85 100,-8 8,-8"/>
<text xml:space="preserve" text-anchor="middle" x="54" y="-67.7" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#666666">Shunts</text>
</g>
<!-- jira -->
<g id="node1" class="node ok">
<title>jira</title>
<polygon fill="#1a1a1a" stroke="#333333" points="204.88,-112 150.88,-112 150.88,-76 204.88,-76 204.88,-112"/>
<text xml:space="preserve" text-anchor="middle" x="177.88" y="-91.08" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#86efac">Jira</text>
</g>
<!-- vein -->
<g id="node1" class="node">
<g id="node5" class="node">
<title>vein</title>
<polygon fill="#1a1a1a" stroke="#333333" points="446,-167 300.25,-167 300.25,-131 446,-131 446,-167"/>
<text xml:space="preserve" text-anchor="middle" x="373.12" y="-152.05" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">Vein</text>
<text xml:space="preserve" text-anchor="middle" x="373.12" y="-138.55" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">stateless API connector</text>
<polygon fill="#1a1a1a" stroke="#333333" points="446,-82 300.25,-82 300.25,-46 446,-46 446,-82"/>
<text xml:space="preserve" text-anchor="middle" x="373.12" y="-67.05" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">Vein</text>
<text xml:space="preserve" text-anchor="middle" x="373.12" y="-53.55" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">stateless API connector</text>
</g>
<!-- jira&#45;&gt;vein -->
<!-- google -->
<g id="node2" class="node accent&#45;text">
<title>google</title>
<polygon fill="#1a1a1a" stroke="#333333" points="204.88,-166 150.88,-166 150.88,-130 204.88,-130 204.88,-166"/>
<text xml:space="preserve" text-anchor="middle" x="177.88" y="-145.07" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d4a574">Google</text>
</g>
<!-- ia -->
<g id="node3" class="node ok">
<title>ia</title>
<polygon fill="#1a1a1a" stroke="#333333" points="204.88,-220 150.88,-220 150.88,-184 204.88,-184 204.88,-220"/>
<text xml:space="preserve" text-anchor="middle" x="177.88" y="-199.07" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#86efac">IA</text>
</g>
<!-- mp -->
<g id="node4" class="node accent&#45;text">
<title>mp</title>
<polygon fill="#1a1a1a" stroke="#333333" points="92,-52 16,-52 16,-16 92,-16 92,-52"/>
<text xml:space="preserve" text-anchor="middle" x="54" y="-31.07" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d4a574">MercadoPago</text>
</g>
<!-- shunt -->
<g id="node8" class="node accent">
<title>shunt</title>
<polygon fill="#1a1a1a" stroke="#d4a574" points="226.75,-58.25 129,-58.25 129,-9.75 226.75,-9.75 226.75,-58.25"/>
<text xml:space="preserve" text-anchor="middle" x="177.88" y="-43.8" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">Shunt</text>
<text xml:space="preserve" text-anchor="middle" x="177.88" y="-30.3" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">fake connector</text>
<text xml:space="preserve" text-anchor="middle" x="177.88" y="-16.8" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">for testing</text>
</g>
<!-- mp&#45;&gt;shunt -->
<!-- pulse -->
<g id="node2" class="node">
<g id="node6" class="node">
<title>pulse</title>
<polygon fill="#1a1a1a" stroke="#333333" points="659.25,-167 522.5,-167 522.5,-131 659.25,-131 659.25,-167"/>
<text xml:space="preserve" text-anchor="middle" x="590.88" y="-152.05" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">Pulse</text>
<text xml:space="preserve" text-anchor="middle" x="590.88" y="-138.55" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">Vein + Room + Depot</text>
<polygon fill="#1a1a1a" stroke="#333333" points="659.25,-82 522.5,-82 522.5,-46 659.25,-46 659.25,-82"/>
<text xml:space="preserve" text-anchor="middle" x="590.88" y="-67.05" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">Pulse</text>
<text xml:space="preserve" text-anchor="middle" x="590.88" y="-53.55" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">Vein + Room + Depot</text>
</g>
<!-- vein&#45;&gt;pulse -->
<g id="edge1" class="edge">
<g id="edge1" class="edge artery">
<title>vein&#45;&gt;pulse</title>
<path fill="none" stroke="#b91c1c" d="M446.27,-149C467.03,-149 489.79,-149 510.95,-149"/>
<polygon fill="#b91c1c" stroke="#b91c1c" points="510.67,-152.5 520.67,-149 510.67,-145.5 510.67,-152.5"/>
<text xml:space="preserve" text-anchor="middle" x="484.25" y="-151.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#a3a3a3">compose</text>
<path fill="none" stroke="#b91c1c" d="M446.27,-64C467.03,-64 489.79,-64 510.95,-64"/>
<polygon fill="#b91c1c" stroke="#b91c1c" points="510.67,-67.5 520.67,-64 510.67,-60.5 510.67,-67.5"/>
<text xml:space="preserve" text-anchor="middle" x="484.25" y="-66.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#fca5a5">compose</text>
</g>
<!-- plexus -->
<g id="node3" class="node">
<g id="node7" class="node">
<title>plexus</title>
<polygon fill="#1a1a1a" stroke="#333333" points="836.5,-173.25 726.75,-173.25 726.75,-124.75 836.5,-124.75 836.5,-173.25"/>
<text xml:space="preserve" text-anchor="middle" x="781.62" y="-158.8" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">Plexus</text>
<text xml:space="preserve" text-anchor="middle" x="781.62" y="-145.3" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">full app: backend</text>
<text xml:space="preserve" text-anchor="middle" x="781.62" y="-131.8" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">+ frontend + DB</text>
<polygon fill="#1a1a1a" stroke="#333333" points="836.5,-88.25 726.75,-88.25 726.75,-39.75 836.5,-39.75 836.5,-88.25"/>
<text xml:space="preserve" text-anchor="middle" x="781.62" y="-73.8" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">Plexus</text>
<text xml:space="preserve" text-anchor="middle" x="781.62" y="-60.3" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">full app: backend</text>
<text xml:space="preserve" text-anchor="middle" x="781.62" y="-46.8" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">+ frontend + DB</text>
</g>
<!-- pulse&#45;&gt;plexus -->
<g id="edge2" class="edge">
<g id="edge2" class="edge artery">
<title>pulse&#45;&gt;plexus</title>
<path fill="none" stroke="#b91c1c" d="M659.48,-149C677.62,-149 697.19,-149 715.21,-149"/>
<polygon fill="#b91c1c" stroke="#b91c1c" points="714.98,-152.5 724.98,-149 714.98,-145.5 714.98,-152.5"/>
<text xml:space="preserve" text-anchor="middle" x="693" y="-151.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#a3a3a3">extend</text>
</g>
<!-- shunt -->
<g id="node4" class="node">
<title>shunt</title>
<polygon fill="#1a1a1a" stroke="#d4a574" points="226.75,-249.25 129,-249.25 129,-200.75 226.75,-200.75 226.75,-249.25"/>
<text xml:space="preserve" text-anchor="middle" x="177.88" y="-234.8" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">Shunt</text>
<text xml:space="preserve" text-anchor="middle" x="177.88" y="-221.3" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">fake connector</text>
<text xml:space="preserve" text-anchor="middle" x="177.88" y="-207.8" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">for testing</text>
<path fill="none" stroke="#b91c1c" d="M659.48,-64C677.62,-64 697.19,-64 715.21,-64"/>
<polygon fill="#b91c1c" stroke="#b91c1c" points="714.98,-67.5 724.98,-64 714.98,-60.5 714.98,-67.5"/>
<text xml:space="preserve" text-anchor="middle" x="693" y="-66.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#fca5a5">extend</text>
</g>
<!-- shunt&#45;&gt;vein -->
<g id="edge3" class="edge">
<g id="edge3" class="edge accent">
<title>shunt&#45;&gt;vein</title>
<path fill="none" stroke="#d4a574" stroke-dasharray="5,2" d="M227.14,-206.02C253.71,-195.57 286.85,-182.54 314.73,-171.57"/>
<polygon fill="#d4a574" stroke="#d4a574" points="315.71,-174.95 323.73,-168.03 313.14,-168.43 315.71,-174.95"/>
<text xml:space="preserve" text-anchor="middle" x="263.5" y="-200.95" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d4a574">replaces</text>
</g>
<!-- jira -->
<g id="node5" class="node">
<title>jira</title>
<polygon fill="#1a1a1a" stroke="#333333" points="204.88,-52 150.88,-52 150.88,-16 204.88,-16 204.88,-52"/>
<text xml:space="preserve" text-anchor="middle" x="177.88" y="-31.07" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#15803d">Jira</text>
</g>
<!-- jira&#45;&gt;vein -->
<!-- google -->
<g id="node6" class="node">
<title>google</title>
<polygon fill="#1a1a1a" stroke="#333333" points="204.88,-106 150.88,-106 150.88,-70 204.88,-70 204.88,-106"/>
<text xml:space="preserve" text-anchor="middle" x="177.88" y="-85.08" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d4a574">Google</text>
</g>
<!-- ia -->
<g id="node7" class="node">
<title>ia</title>
<polygon fill="#1a1a1a" stroke="#333333" points="204.88,-160 150.88,-160 150.88,-124 204.88,-124 204.88,-160"/>
<text xml:space="preserve" text-anchor="middle" x="177.88" y="-139.07" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#15803d">IA</text>
<path fill="none" stroke="#d4a574" stroke-dasharray="5,2" d="M227.14,-41.49C245.81,-44.39 267.72,-47.79 288.78,-51.06"/>
<polygon fill="#d4a574" stroke="#d4a574" points="288.11,-54.5 298.53,-52.58 289.18,-47.58 288.11,-54.5"/>
<text xml:space="preserve" text-anchor="middle" x="263.5" y="-52.6" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d4a574">replaces</text>
</g>
<!-- mp -->
<g id="node8" class="node">
<title>mp</title>
<polygon fill="#1a1a1a" stroke="#333333" points="92,-243 16,-243 16,-207 92,-207 92,-243"/>
<text xml:space="preserve" text-anchor="middle" x="54" y="-222.07" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d4a574">MercadoPago</text>
</g>
<!-- mp&#45;&gt;shunt -->
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@@ -1,43 +1,37 @@
digraph cfg_gen_flow {
bgcolor="#0a0a0a"
rankdir=LR
fontname="Helvetica"
node [fontname="Helvetica" fontsize=11 style=filled color="#333" fontcolor="#e5e5e5" shape=box]
edge [fontname="Helvetica" fontsize=9 fontcolor="#a3a3a3" color="#d4a574"]
node [fontname="Helvetica" fontsize=11 style=filled shape=box]
edge [class="accent" fontname="Helvetica" fontsize=9]
label="Build Flow — cfg/ to gen/"
labelloc=t
fontsize=14
fontcolor="#d4a574"
// Source
subgraph cluster_source {
label="Source (committed)"
style=dashed
color="#333"
fontcolor="#666"
core [label="soleprint/\ncore framework" fillcolor="#1a1a1a"]
cfg [label="cfg/<room>/\nroom config" fillcolor="#1a1a1a"]
core [label="soleprint/\ncore framework"]
cfg [label="cfg/<room>/\nroom config"]
}
// Build
build [label="build.py\n--cfg <room>" fillcolor="#1a1a1a" color="#d4a574" shape=component]
build [class="accent" label="build.py\n--cfg <room>" shape=component]
// Output
subgraph cluster_output {
label="Output (generated, gitignored)"
style=dashed
color="#333"
fontcolor="#666"
gen_spr [label="gen/<room>/soleprint/\ncore + room merged" fillcolor="#1a1a1a"]
gen_app [label="gen/<room>/<app>/\ncloned repos" fillcolor="#1a1a1a"]
gen_link [label="gen/<room>/link/\nDB bridge" fillcolor="#1a1a1a"]
gen_spr [label="gen/<room>/soleprint/\ncore + room merged"]
gen_app [label="gen/<room>/<app>/\ncloned repos"]
gen_link [label="gen/<room>/link/\nDB bridge"]
}
// Run
docker [label="docker compose up" fillcolor="#1a1a1a" shape=component]
docker [label="docker compose up" shape=component]
// Flow
core -> build

View File

@@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 14.1.2 (0)
-->
<!-- Title: cfg_gen_flow Pages: 1 -->
<svg width="661pt" height="232pt"
viewBox="0.00 0.00 661.00 232.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 227.75)">
<title>cfg_gen_flow</title>
<polygon fill="#ffffff" stroke="none" points="-4,4 -4,-227.75 657.25,-227.75 657.25,4 -4,4"/>
<text xml:space="preserve" text-anchor="middle" x="326.62" y="-206.45" font-family="Arial" font-size="14.00" fill="#1f2933">Build Flow — cfg/ to gen/</text>
<g id="clust1" class="cluster">
<title>cluster_source</title>
<path fill="#f5f7fa" stroke="#cbd2d9" stroke-dasharray="5,2" d="M12,-62C12,-62 123.25,-62 123.25,-62 129.25,-62 135.25,-68 135.25,-74 135.25,-74 135.25,-180 135.25,-180 135.25,-186 129.25,-192 123.25,-192 123.25,-192 12,-192 12,-192 6,-192 0,-186 0,-180 0,-180 0,-74 0,-74 0,-68 6,-62 12,-62"/>
<text xml:space="preserve" text-anchor="middle" x="67.62" y="-174.7" font-family="Arial" font-size="14.00" fill="#616e7c">Source (committed)</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_output</title>
<path fill="#f5f7fa" stroke="#cbd2d9" stroke-dasharray="5,2" d="M329,-8C329,-8 502.5,-8 502.5,-8 508.5,-8 514.5,-14 514.5,-20 514.5,-20 514.5,-180 514.5,-180 514.5,-186 508.5,-192 502.5,-192 502.5,-192 329,-192 329,-192 323,-192 317,-186 317,-180 317,-180 317,-20 317,-20 317,-14 323,-8 329,-8"/>
<text xml:space="preserve" text-anchor="middle" x="415.75" y="-174.7" font-family="Arial" font-size="14.00" fill="#616e7c">Output (generated, gitignored)</text>
</g>
<!-- core -->
<g id="node1" class="node">
<title>core</title>
<path fill="#ffffff" stroke="#9aa5b1" d="M100.62,-160C100.62,-160 33.62,-160 33.62,-160 27.62,-160 21.62,-154 21.62,-148 21.62,-148 21.62,-136 21.62,-136 21.62,-130 27.62,-124 33.62,-124 33.62,-124 100.62,-124 100.62,-124 106.62,-124 112.62,-130 112.62,-136 112.62,-136 112.62,-148 112.62,-148 112.62,-154 106.62,-160 100.62,-160"/>
<text xml:space="preserve" text-anchor="middle" x="67.12" y="-145.05" font-family="Arial" font-size="11.00" fill="#1f2933">soleprint/</text>
<text xml:space="preserve" text-anchor="middle" x="67.12" y="-131.55" font-family="Arial" font-size="11.00" fill="#1f2933">core framework</text>
</g>
<!-- build -->
<g id="node6" class="node accent">
<title>build</title>
<polygon fill="#d6e4ff" stroke="#3a7dff" points="243.25,-106 164.25,-106 164.25,-102 160.25,-102 160.25,-98 164.25,-98 164.25,-78 160.25,-78 160.25,-74 164.25,-74 164.25,-70 243.25,-70 243.25,-106"/>
<polyline fill="none" stroke="#3a7dff" points="164.25,-102 168.25,-102 168.25,-98 164.25,-98"/>
<polyline fill="none" stroke="#3a7dff" points="164.25,-78 168.25,-78 168.25,-74 164.25,-74"/>
<text xml:space="preserve" text-anchor="middle" x="203.75" y="-91.05" font-family="Arial" font-size="11.00" fill="#1f2933">build.py</text>
<text xml:space="preserve" text-anchor="middle" x="203.75" y="-77.55" font-family="Arial" font-size="11.00" fill="#1f2933">&#45;&#45;cfg &lt;room&gt;</text>
</g>
<!-- core&#45;&gt;build -->
<g id="edge1" class="edge accent">
<title>core&#45;&gt;build</title>
<path fill="none" stroke="#3a7dff" d="M112.95,-124.02C126.89,-118.43 142.29,-112.25 156.33,-106.62"/>
<polygon fill="#3a7dff" stroke="#3a7dff" points="156.99,-108.99 162.58,-104.11 155.17,-104.45 156.99,-108.99"/>
</g>
<!-- cfg -->
<g id="node2" class="node">
<title>cfg</title>
<path fill="#ffffff" stroke="#9aa5b1" d="M92.38,-106C92.38,-106 41.88,-106 41.88,-106 35.88,-106 29.88,-100 29.88,-94 29.88,-94 29.88,-82 29.88,-82 29.88,-76 35.88,-70 41.88,-70 41.88,-70 92.38,-70 92.38,-70 98.38,-70 104.38,-76 104.38,-82 104.38,-82 104.38,-94 104.38,-94 104.38,-100 98.38,-106 92.38,-106"/>
<text xml:space="preserve" text-anchor="middle" x="67.12" y="-91.05" font-family="Arial" font-size="11.00" fill="#1f2933">cfg/&lt;room&gt;/</text>
<text xml:space="preserve" text-anchor="middle" x="67.12" y="-77.55" font-family="Arial" font-size="11.00" fill="#1f2933">room config</text>
</g>
<!-- cfg&#45;&gt;build -->
<g id="edge2" class="edge accent">
<title>cfg&#45;&gt;build</title>
<path fill="none" stroke="#3a7dff" d="M104.56,-88C120.35,-88 139.04,-88 155.85,-88"/>
<polygon fill="#3a7dff" stroke="#3a7dff" points="155.42,-90.45 162.42,-88 155.42,-85.55 155.42,-90.45"/>
</g>
<!-- gen_spr -->
<g id="node3" class="node">
<title>gen_spr</title>
<path fill="#ffffff" stroke="#9aa5b1" d="M464.12,-160C464.12,-160 366.38,-160 366.38,-160 360.38,-160 354.38,-154 354.38,-148 354.38,-148 354.38,-136 354.38,-136 354.38,-130 360.38,-124 366.38,-124 366.38,-124 464.12,-124 464.12,-124 470.12,-124 476.12,-130 476.12,-136 476.12,-136 476.12,-148 476.12,-148 476.12,-154 470.12,-160 464.12,-160"/>
<text xml:space="preserve" text-anchor="middle" x="415.25" y="-145.05" font-family="Arial" font-size="11.00" fill="#1f2933">gen/&lt;room&gt;/soleprint/</text>
<text xml:space="preserve" text-anchor="middle" x="415.25" y="-131.55" font-family="Arial" font-size="11.00" fill="#1f2933">core + room merged</text>
</g>
<!-- docker -->
<g id="node7" class="node">
<title>docker</title>
<polygon fill="#ffffff" stroke="#9aa5b1" points="653.25,-160 543.5,-160 543.5,-156 539.5,-156 539.5,-152 543.5,-152 543.5,-132 539.5,-132 539.5,-128 543.5,-128 543.5,-124 653.25,-124 653.25,-160"/>
<polyline fill="none" stroke="#9aa5b1" points="543.5,-156 547.5,-156 547.5,-152 543.5,-152"/>
<polyline fill="none" stroke="#9aa5b1" points="543.5,-132 547.5,-132 547.5,-128 543.5,-128"/>
<text xml:space="preserve" text-anchor="middle" x="598.38" y="-138.3" font-family="Arial" font-size="11.00" fill="#1f2933">docker compose up</text>
</g>
<!-- gen_spr&#45;&gt;docker -->
<g id="edge6" class="edge accent">
<title>gen_spr&#45;&gt;docker</title>
<path fill="none" stroke="#3a7dff" d="M476.56,-142C495.21,-142 515.8,-142 534.61,-142"/>
<polygon fill="#3a7dff" stroke="#3a7dff" points="534.6,-144.45 541.6,-142 534.6,-139.55 534.6,-144.45"/>
</g>
<!-- gen_app -->
<g id="node4" class="node">
<title>gen_app</title>
<path fill="#ffffff" stroke="#9aa5b1" d="M459.62,-106C459.62,-106 370.88,-106 370.88,-106 364.88,-106 358.88,-100 358.88,-94 358.88,-94 358.88,-82 358.88,-82 358.88,-76 364.88,-70 370.88,-70 370.88,-70 459.62,-70 459.62,-70 465.62,-70 471.62,-76 471.62,-82 471.62,-82 471.62,-94 471.62,-94 471.62,-100 465.62,-106 459.62,-106"/>
<text xml:space="preserve" text-anchor="middle" x="415.25" y="-91.05" font-family="Arial" font-size="11.00" fill="#1f2933">gen/&lt;room&gt;/&lt;app&gt;/</text>
<text xml:space="preserve" text-anchor="middle" x="415.25" y="-77.55" font-family="Arial" font-size="11.00" fill="#1f2933">cloned repos</text>
</g>
<!-- gen_link -->
<g id="node5" class="node">
<title>gen_link</title>
<path fill="#ffffff" stroke="#9aa5b1" d="M451.75,-52C451.75,-52 378.75,-52 378.75,-52 372.75,-52 366.75,-46 366.75,-40 366.75,-40 366.75,-28 366.75,-28 366.75,-22 372.75,-16 378.75,-16 378.75,-16 451.75,-16 451.75,-16 457.75,-16 463.75,-22 463.75,-28 463.75,-28 463.75,-40 463.75,-40 463.75,-46 457.75,-52 451.75,-52"/>
<text xml:space="preserve" text-anchor="middle" x="415.25" y="-37.05" font-family="Arial" font-size="11.00" fill="#1f2933">gen/&lt;room&gt;/link/</text>
<text xml:space="preserve" text-anchor="middle" x="415.25" y="-23.55" font-family="Arial" font-size="11.00" fill="#1f2933">DB bridge</text>
</g>
<!-- build&#45;&gt;gen_spr -->
<g id="edge3" class="edge accent">
<title>build&#45;&gt;gen_spr</title>
<path fill="none" stroke="#3a7dff" d="M243.64,-98.38C249.54,-99.94 255.55,-101.52 261.25,-103 288.9,-110.19 319.39,-117.99 345.83,-124.71"/>
<polygon fill="#3a7dff" stroke="#3a7dff" points="345.05,-127.04 352.43,-126.39 346.25,-122.29 345.05,-127.04"/>
</g>
<!-- build&#45;&gt;gen_app -->
<g id="edge4" class="edge accent">
<title>build&#45;&gt;gen_app</title>
<path fill="none" stroke="#3a7dff" stroke-dasharray="5,2" d="M243.66,-88C273.57,-88 315.52,-88 350.33,-88"/>
<polygon fill="#3a7dff" stroke="#3a7dff" points="350.04,-90.45 357.04,-88 350.04,-85.55 350.04,-90.45"/>
<text xml:space="preserve" text-anchor="middle" x="284.12" y="-89.95" font-family="Arial" font-size="9.00" fill="#3a7dff">if managed</text>
</g>
<!-- build&#45;&gt;gen_link -->
<g id="edge5" class="edge accent">
<title>build&#45;&gt;gen_link</title>
<path fill="none" stroke="#3a7dff" stroke-dasharray="5,2" d="M243.66,-77.97C275.9,-69.66 322.12,-57.75 358.32,-48.42"/>
<polygon fill="#3a7dff" stroke="#3a7dff" points="358.72,-50.84 364.88,-46.72 357.49,-46.1 358.72,-50.84"/>
<text xml:space="preserve" text-anchor="middle" x="284.12" y="-73.99" font-family="Arial" font-size="9.00" fill="#3a7dff">if managed</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@@ -28,7 +28,7 @@
<text xml:space="preserve" text-anchor="middle" x="75.75" y="-131.55" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">core framework</text>
</g>
<!-- build -->
<g id="node3" class="node">
<g id="node6" class="node accent">
<title>build</title>
<polygon fill="#1a1a1a" stroke="#d4a574" points="271.75,-106 181.5,-106 181.5,-102 177.5,-102 177.5,-98 181.5,-98 181.5,-78 177.5,-78 177.5,-74 181.5,-74 181.5,-70 271.75,-70 271.75,-106"/>
<polyline fill="none" stroke="#d4a574" points="181.5,-102 185.5,-102 185.5,-98 181.5,-98"/>
@@ -37,7 +37,7 @@
<text xml:space="preserve" text-anchor="middle" x="226.62" y="-77.55" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">&#45;&#45;cfg &lt;room&gt;</text>
</g>
<!-- core&#45;&gt;build -->
<g id="edge1" class="edge">
<g id="edge1" class="edge accent">
<title>core&#45;&gt;build</title>
<path fill="none" stroke="#d4a574" d="M127.3,-124.07C135.72,-121.06 144.36,-117.95 152.5,-115 158.3,-112.89 164.36,-110.68 170.4,-108.47"/>
<polygon fill="#d4a574" stroke="#d4a574" points="171.59,-111.76 179.76,-105.02 169.17,-105.19 171.59,-111.76"/>
@@ -50,65 +50,65 @@
<text xml:space="preserve" text-anchor="middle" x="75.75" y="-77.55" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">room config</text>
</g>
<!-- cfg&#45;&gt;build -->
<g id="edge2" class="edge">
<g id="edge2" class="edge accent">
<title>cfg&#45;&gt;build</title>
<path fill="none" stroke="#d4a574" d="M119.41,-88C135.12,-88 153.15,-88 169.84,-88"/>
<polygon fill="#d4a574" stroke="#d4a574" points="169.63,-91.5 179.63,-88 169.63,-84.5 169.63,-91.5"/>
</g>
<!-- gen_spr -->
<g id="node4" class="node">
<g id="node3" class="node">
<title>gen_spr</title>
<polygon fill="#1a1a1a" stroke="#333333" points="536.5,-160 394.5,-160 394.5,-124 536.5,-124 536.5,-160"/>
<text xml:space="preserve" text-anchor="middle" x="465.5" y="-145.05" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">gen/&lt;room&gt;/soleprint/</text>
<text xml:space="preserve" text-anchor="middle" x="465.5" y="-131.55" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">core + room merged</text>
</g>
<!-- build&#45;&gt;gen_spr -->
<g id="edge3" class="edge">
<title>build&#45;&gt;gen_spr</title>
<path fill="none" stroke="#d4a574" d="M272.06,-98.83C278,-100.25 284.02,-101.67 289.75,-103 320.22,-110.08 353.74,-117.62 383.21,-124.16"/>
<polygon fill="#d4a574" stroke="#d4a574" points="382.29,-127.55 392.81,-126.29 383.8,-120.71 382.29,-127.55"/>
<!-- docker -->
<g id="node7" class="node">
<title>docker</title>
<polygon fill="#1a1a1a" stroke="#333333" points="733.5,-160 610.25,-160 610.25,-156 606.25,-156 606.25,-152 610.25,-152 610.25,-132 606.25,-132 606.25,-128 610.25,-128 610.25,-124 733.5,-124 733.5,-160"/>
<polyline fill="none" stroke="#333333" points="610.25,-156 614.25,-156 614.25,-152 610.25,-152"/>
<polyline fill="none" stroke="#333333" points="610.25,-132 614.25,-132 614.25,-128 610.25,-128"/>
<text xml:space="preserve" text-anchor="middle" x="671.88" y="-138.3" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">docker compose up</text>
</g>
<!-- gen_spr&#45;&gt;docker -->
<g id="edge6" class="edge accent">
<title>gen_spr&#45;&gt;docker</title>
<path fill="none" stroke="#d4a574" d="M536.83,-142C556.81,-142 578.59,-142 598.68,-142"/>
<polygon fill="#d4a574" stroke="#d4a574" points="598.44,-145.5 608.44,-142 598.44,-138.5 598.44,-145.5"/>
</g>
<!-- gen_app -->
<g id="node5" class="node">
<g id="node4" class="node">
<title>gen_app</title>
<polygon fill="#1a1a1a" stroke="#333333" points="531.62,-106 399.38,-106 399.38,-70 531.62,-70 531.62,-106"/>
<text xml:space="preserve" text-anchor="middle" x="465.5" y="-91.05" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">gen/&lt;room&gt;/&lt;app&gt;/</text>
<text xml:space="preserve" text-anchor="middle" x="465.5" y="-77.55" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">cloned repos</text>
</g>
<!-- build&#45;&gt;gen_app -->
<g id="edge4" class="edge">
<title>build&#45;&gt;gen_app</title>
<path fill="none" stroke="#d4a574" stroke-dasharray="5,2" d="M272.18,-88C304.75,-88 349.72,-88 388,-88"/>
<polygon fill="#d4a574" stroke="#d4a574" points="387.6,-91.5 397.6,-88 387.6,-84.5 387.6,-91.5"/>
<text xml:space="preserve" text-anchor="middle" x="315.25" y="-90.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#a3a3a3">if managed</text>
</g>
<!-- gen_link -->
<g id="node6" class="node">
<g id="node5" class="node">
<title>gen_link</title>
<polygon fill="#1a1a1a" stroke="#333333" points="521.88,-52 409.12,-52 409.12,-16 521.88,-16 521.88,-52"/>
<text xml:space="preserve" text-anchor="middle" x="465.5" y="-37.05" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">gen/&lt;room&gt;/link/</text>
<text xml:space="preserve" text-anchor="middle" x="465.5" y="-23.55" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">DB bridge</text>
</g>
<!-- build&#45;&gt;gen_spr -->
<g id="edge3" class="edge accent">
<title>build&#45;&gt;gen_spr</title>
<path fill="none" stroke="#d4a574" d="M272.06,-98.83C278,-100.25 284.02,-101.67 289.75,-103 320.22,-110.08 353.74,-117.62 383.21,-124.16"/>
<polygon fill="#d4a574" stroke="#d4a574" points="382.29,-127.55 392.81,-126.29 383.8,-120.71 382.29,-127.55"/>
</g>
<!-- build&#45;&gt;gen_app -->
<g id="edge4" class="edge accent">
<title>build&#45;&gt;gen_app</title>
<path fill="none" stroke="#d4a574" stroke-dasharray="5,2" d="M272.18,-88C304.75,-88 349.72,-88 388,-88"/>
<polygon fill="#d4a574" stroke="#d4a574" points="387.6,-91.5 397.6,-88 387.6,-84.5 387.6,-91.5"/>
<text xml:space="preserve" text-anchor="middle" x="315.25" y="-90.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d4a574">if managed</text>
</g>
<!-- build&#45;&gt;gen_link -->
<g id="edge5" class="edge">
<g id="edge5" class="edge accent">
<title>build&#45;&gt;gen_link</title>
<path fill="none" stroke="#d4a574" stroke-dasharray="5,2" d="M272.05,-76.97C278,-75.53 284.02,-74.09 289.75,-72.75 325.35,-64.44 365.13,-55.58 397.73,-48.44"/>
<polygon fill="#d4a574" stroke="#d4a574" points="398.44,-51.86 407.46,-46.31 396.95,-45.03 398.44,-51.86"/>
<text xml:space="preserve" text-anchor="middle" x="315.25" y="-75.45" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#a3a3a3">if managed</text>
</g>
<!-- docker -->
<g id="node7" class="node">
<title>docker</title>
<polygon fill="#1a1a1a" stroke="#333333" points="733.5,-160 610.25,-160 610.25,-156 606.25,-156 606.25,-152 610.25,-152 610.25,-132 606.25,-132 606.25,-128 610.25,-128 610.25,-124 733.5,-124 733.5,-160"/>
<polyline fill="none" stroke="#333333" points="610.25,-156 614.25,-156 614.25,-152 610.25,-152"/>
<polyline fill="none" stroke="#333333" points="610.25,-132 614.25,-132 614.25,-128 610.25,-128"/>
<text xml:space="preserve" text-anchor="middle" x="671.88" y="-138.3" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">docker compose up</text>
</g>
<!-- gen_spr&#45;&gt;docker -->
<g id="edge6" class="edge">
<title>gen_spr&#45;&gt;docker</title>
<path fill="none" stroke="#d4a574" d="M536.83,-142C556.81,-142 578.59,-142 598.68,-142"/>
<polygon fill="#d4a574" stroke="#d4a574" points="598.44,-145.5 608.44,-142 598.44,-138.5 598.44,-145.5"/>
<text xml:space="preserve" text-anchor="middle" x="315.25" y="-75.45" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d4a574">if managed</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

69
docs/graphs/render.sh Executable file
View File

@@ -0,0 +1,69 @@
#!/usr/bin/env bash
# Render every .dot through every theme.
#
# ./render.sh # all graphs, all themes
# ./render.sh lucid # one theme
# ./render.sh dark system_overview
#
# The sources carry structure and meaning; themes/*.gvpr carry palette. gvpr
# rewrites the parsed graph, so it wins over anything a .dot sets inline — which
# is what lets one source render in several looks without being edited.
#
# Output naming: the default theme writes <name>.svg, because that is what
# docs/data/en/*.md already links to and those links should keep working. Every
# other theme writes <name>.<theme>.svg.
#
# Why baked rather than CSS: both docs sites embed graphs with <img src=...>,
# which makes the SVG a separate document that the page's stylesheet cannot
# reach. Colour has to be in the file.
set -euo pipefail
cd "$(dirname "$0")"
DEFAULT_THEME=dark
if ! command -v dot >/dev/null 2>&1 || ! command -v gvpr >/dev/null 2>&1; then
echo "graphviz not found — install with: sudo apt install graphviz" >&2
echo "(the committed .svg files already work; this is only needed to re-render)" >&2
exit 1
fi
theme_arg="${1:-}"
graph_arg="${2:-}"
themes=()
if [ -n "$theme_arg" ]; then
if [ ! -f "themes/${theme_arg}.gvpr" ]; then
echo "no such theme: themes/${theme_arg}.gvpr" >&2
echo "available: $(ls themes/*.gvpr 2>/dev/null | xargs -n1 basename | sed 's/\.gvpr$//' | tr '\n' ' ')" >&2
exit 1
fi
themes=("$theme_arg")
else
for t in themes/*.gvpr; do themes+=("$(basename "$t" .gvpr)"); done
fi
shopt -s nullglob
sources=(*.dot)
if [ -n "$graph_arg" ]; then
sources=("${graph_arg%.dot}.dot")
[ -f "${sources[0]}" ] || { echo "no such graph: ${sources[0]}" >&2; exit 1; }
fi
[ ${#sources[@]} -gt 0 ] || { echo "no .dot files here"; exit 0; }
for theme in "${themes[@]}"; do
for src in "${sources[@]}"; do
base="${src%.dot}"
if [ "$theme" = "$DEFAULT_THEME" ]; then
out="${base}.svg"
else
out="${base}.${theme}.svg"
fi
# One pipeline, so a gvpr failure fails the whole render rather than
# silently writing a half-themed file.
gvpr -c -f "themes/${theme}.gvpr" "$src" | dot -Tsvg -o "$out"
printf " %-14s %s\n" "$theme" "$out"
done
done
echo
echo "done — ${#sources[@]} graph(s) x ${#themes[@]} theme(s)"

View File

@@ -1,22 +1,20 @@
digraph room_layers {
bgcolor="#0a0a0a"
rankdir=TB
fontname="Helvetica"
node [fontname="Helvetica" fontsize=10 style=filled color="#333" fontcolor="#e5e5e5" shape=record]
edge [fontname="Helvetica" fontsize=9 fontcolor="#a3a3a3" color="#666"]
node [fontname="Helvetica" fontsize=10 style=filled shape=record]
edge [fontname="Helvetica" fontsize=9]
label="Room Layers — init wizard"
labelloc=t
fontsize=14
fontcolor="#d4a574"
l0 [label="{Layer 0 | Config + Data | config.json · data/*.json}" fillcolor="#1a1a1a" color="#d4a574"]
l1 [label="{Layer 1 | Docker | soleprint/docker-compose.yml · .env}" fillcolor="#1a1a1a"]
l2 [label="{Layer 2 | Managed App | docker-compose.yml · Dockerfiles · .env}" fillcolor="#1a1a1a"]
l3 [label="{Layer 3 | Link | link/main.py · adapters/ · Dockerfile}" fillcolor="#1a1a1a"]
l4 [label="{Layer 4 | Scripts | ctrl/start.sh · stop.sh · status.sh · logs.sh}" fillcolor="#1a1a1a"]
l5 [label="{Layer 5 | Systems | tester/environments.json · tests/}" fillcolor="#1a1a1a"]
l6 [label="{Layer 6 | Nginx | nginx/local.conf · docker-compose.nginx.yml}" fillcolor="#1a1a1a"]
l0 [class="accent" label="{Layer 0 | Config + Data | config.json · data/*.json}"]
l1 [label="{Layer 1 | Docker | soleprint/docker-compose.yml · .env}"]
l2 [label="{Layer 2 | Managed App | docker-compose.yml · Dockerfiles · .env}"]
l3 [label="{Layer 3 | Link | link/main.py · adapters/ · Dockerfile}"]
l4 [label="{Layer 4 | Scripts | ctrl/start.sh · stop.sh · status.sh · logs.sh}"]
l5 [label="{Layer 5 | Systems | tester/environments.json · tests/}"]
l6 [label="{Layer 6 | Nginx | nginx/local.conf · docker-compose.nginx.yml}"]
l0 -> l1 [label="required"]
l1 -> l2 [label="if managed"]
@@ -26,6 +24,6 @@ digraph room_layers {
l5 -> l6 [label="if frontend"]
// Annotations
note_req [label="every room" fillcolor="#0a0a0a" fontcolor="#d4a574" color="#0a0a0a" shape=plaintext fontsize=9]
note_req [class="accent" label="every room" shape=plaintext fontsize=9]
note_req -> l0 [style=invis]
}

View File

@@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 14.1.2 (0)
-->
<!-- Title: room_layers Pages: 1 -->
<svg width="423pt" height="596pt"
viewBox="0.00 0.00 423.00 596.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 591.75)">
<title>room_layers</title>
<polygon fill="#ffffff" stroke="none" points="-4,4 -4,-591.75 418.62,-591.75 418.62,4 -4,4"/>
<text xml:space="preserve" text-anchor="middle" x="207.31" y="-570.45" font-family="Arial" font-size="14.00" fill="#1f2933">Room Layers — init wizard</text>
<!-- l0 -->
<g id="node1" class="node accent">
<title>l0</title>
<polygon fill="#d6e4ff" stroke="#3a7dff" points="143.12,-430.5 143.12,-490.5 261.88,-490.5 261.88,-430.5 143.12,-430.5"/>
<text xml:space="preserve" text-anchor="middle" x="202.5" y="-477" font-family="Arial" font-size="10.00" fill="#1f2933">Layer 0</text>
<polyline fill="none" stroke="#3a7dff" points="143.12,-470.5 261.88,-470.5"/>
<text xml:space="preserve" text-anchor="middle" x="202.5" y="-457" font-family="Arial" font-size="10.00" fill="#1f2933">Config + Data</text>
<polyline fill="none" stroke="#3a7dff" points="143.12,-450.5 261.88,-450.5"/>
<text xml:space="preserve" text-anchor="middle" x="202.5" y="-437" font-family="Arial" font-size="10.00" fill="#1f2933">config.json · data/*.json</text>
</g>
<!-- l1 -->
<g id="node2" class="node">
<title>l1</title>
<polygon fill="#ffffff" stroke="#9aa5b1" points="115.38,-323 115.38,-383 289.62,-383 289.62,-323 115.38,-323"/>
<text xml:space="preserve" text-anchor="middle" x="202.5" y="-369.5" font-family="Arial" font-size="10.00" fill="#1f2933">Layer 1</text>
<polyline fill="none" stroke="#9aa5b1" points="115.38,-363 289.62,-363"/>
<text xml:space="preserve" text-anchor="middle" x="202.5" y="-349.5" font-family="Arial" font-size="10.00" fill="#1f2933">Docker</text>
<polyline fill="none" stroke="#9aa5b1" points="115.38,-343 289.62,-343"/>
<text xml:space="preserve" text-anchor="middle" x="202.5" y="-329.5" font-family="Arial" font-size="10.00" fill="#1f2933">soleprint/docker&#45;compose.yml · .env</text>
</g>
<!-- l0&#45;&gt;l1 -->
<g id="edge1" class="edge">
<title>l0&#45;&gt;l1</title>
<path fill="none" stroke="#9aa5b1" d="M202.5,-430.11C202.5,-418.22 202.5,-404.35 202.5,-391.73"/>
<polygon fill="#9aa5b1" stroke="#9aa5b1" points="204.95,-391.74 202.5,-384.74 200.05,-391.74 204.95,-391.74"/>
<text xml:space="preserve" text-anchor="middle" x="219.75" y="-403.45" font-family="Arial" font-size="9.00" fill="#616e7c">required</text>
</g>
<!-- l2 -->
<g id="node3" class="node">
<title>l2</title>
<polygon fill="#ffffff" stroke="#9aa5b1" points="0,-215.5 0,-275.5 193,-275.5 193,-215.5 0,-215.5"/>
<text xml:space="preserve" text-anchor="middle" x="96.5" y="-262" font-family="Arial" font-size="10.00" fill="#1f2933">Layer 2</text>
<polyline fill="none" stroke="#9aa5b1" points="0,-255.5 193,-255.5"/>
<text xml:space="preserve" text-anchor="middle" x="96.5" y="-242" font-family="Arial" font-size="10.00" fill="#1f2933">Managed App</text>
<polyline fill="none" stroke="#9aa5b1" points="0,-235.5 193,-235.5"/>
<text xml:space="preserve" text-anchor="middle" x="96.5" y="-222" font-family="Arial" font-size="10.00" fill="#1f2933">docker&#45;compose.yml · Dockerfiles · .env</text>
</g>
<!-- l1&#45;&gt;l2 -->
<g id="edge2" class="edge">
<title>l1&#45;&gt;l2</title>
<path fill="none" stroke="#9aa5b1" d="M172.97,-322.61C160.22,-309.92 145.2,-294.97 131.86,-281.7"/>
<polygon fill="#9aa5b1" stroke="#9aa5b1" points="133.63,-280 126.94,-276.8 130.17,-283.47 133.63,-280"/>
<text xml:space="preserve" text-anchor="middle" x="175.55" y="-295.95" font-family="Arial" font-size="9.00" fill="#616e7c">if managed</text>
</g>
<!-- l4 -->
<g id="node4" class="node">
<title>l4</title>
<polygon fill="#ffffff" stroke="#9aa5b1" points="211.12,-215.5 211.12,-275.5 407.88,-275.5 407.88,-215.5 211.12,-215.5"/>
<text xml:space="preserve" text-anchor="middle" x="309.5" y="-262" font-family="Arial" font-size="10.00" fill="#1f2933">Layer 4</text>
<polyline fill="none" stroke="#9aa5b1" points="211.12,-255.5 407.88,-255.5"/>
<text xml:space="preserve" text-anchor="middle" x="309.5" y="-242" font-family="Arial" font-size="10.00" fill="#1f2933">Scripts</text>
<polyline fill="none" stroke="#9aa5b1" points="211.12,-235.5 407.88,-235.5"/>
<text xml:space="preserve" text-anchor="middle" x="309.5" y="-222" font-family="Arial" font-size="10.00" fill="#1f2933">ctrl/start.sh · stop.sh · status.sh · logs.sh</text>
</g>
<!-- l1&#45;&gt;l4 -->
<g id="edge3" class="edge">
<title>l1&#45;&gt;l4</title>
<path fill="none" stroke="#9aa5b1" d="M232.31,-322.61C245.18,-309.92 260.34,-294.97 273.8,-281.7"/>
<polygon fill="#9aa5b1" stroke="#9aa5b1" points="275.51,-283.45 278.78,-276.79 272.07,-279.96 275.51,-283.45"/>
<text xml:space="preserve" text-anchor="middle" x="275.71" y="-295.95" font-family="Arial" font-size="9.00" fill="#616e7c">optional</text>
</g>
<!-- l3 -->
<g id="node5" class="node">
<title>l3</title>
<polygon fill="#ffffff" stroke="#9aa5b1" points="10.88,-108 10.88,-168 182.12,-168 182.12,-108 10.88,-108"/>
<text xml:space="preserve" text-anchor="middle" x="96.5" y="-154.5" font-family="Arial" font-size="10.00" fill="#1f2933">Layer 3</text>
<polyline fill="none" stroke="#9aa5b1" points="10.88,-148 182.12,-148"/>
<text xml:space="preserve" text-anchor="middle" x="96.5" y="-134.5" font-family="Arial" font-size="10.00" fill="#1f2933">Link</text>
<polyline fill="none" stroke="#9aa5b1" points="10.88,-128 182.12,-128"/>
<text xml:space="preserve" text-anchor="middle" x="96.5" y="-114.5" font-family="Arial" font-size="10.00" fill="#1f2933">link/main.py · adapters/ · Dockerfile</text>
</g>
<!-- l2&#45;&gt;l3 -->
<g id="edge4" class="edge">
<title>l2&#45;&gt;l3</title>
<path fill="none" stroke="#9aa5b1" d="M96.5,-215.11C96.5,-203.22 96.5,-189.35 96.5,-176.73"/>
<polygon fill="#9aa5b1" stroke="#9aa5b1" points="98.95,-176.74 96.5,-169.74 94.05,-176.74 98.95,-176.74"/>
<text xml:space="preserve" text-anchor="middle" x="113" y="-188.45" font-family="Arial" font-size="9.00" fill="#616e7c">optional</text>
</g>
<!-- l5 -->
<g id="node6" class="node">
<title>l5</title>
<polygon fill="#ffffff" stroke="#9aa5b1" points="231,-108 231,-168 388,-168 388,-108 231,-108"/>
<text xml:space="preserve" text-anchor="middle" x="309.5" y="-154.5" font-family="Arial" font-size="10.00" fill="#1f2933">Layer 5</text>
<polyline fill="none" stroke="#9aa5b1" points="231,-148 388,-148"/>
<text xml:space="preserve" text-anchor="middle" x="309.5" y="-134.5" font-family="Arial" font-size="10.00" fill="#1f2933">Systems</text>
<polyline fill="none" stroke="#9aa5b1" points="231,-128 388,-128"/>
<text xml:space="preserve" text-anchor="middle" x="309.5" y="-114.5" font-family="Arial" font-size="10.00" fill="#1f2933">tester/environments.json · tests/</text>
</g>
<!-- l4&#45;&gt;l5 -->
<g id="edge5" class="edge">
<title>l4&#45;&gt;l5</title>
<path fill="none" stroke="#9aa5b1" d="M309.5,-215.11C309.5,-203.22 309.5,-189.35 309.5,-176.73"/>
<polygon fill="#9aa5b1" stroke="#9aa5b1" points="311.95,-176.74 309.5,-169.74 307.05,-176.74 311.95,-176.74"/>
<text xml:space="preserve" text-anchor="middle" x="326" y="-188.45" font-family="Arial" font-size="9.00" fill="#616e7c">optional</text>
</g>
<!-- l6 -->
<g id="node7" class="node">
<title>l6</title>
<polygon fill="#ffffff" stroke="#9aa5b1" points="204.38,-0.5 204.38,-60.5 414.62,-60.5 414.62,-0.5 204.38,-0.5"/>
<text xml:space="preserve" text-anchor="middle" x="309.5" y="-47" font-family="Arial" font-size="10.00" fill="#1f2933">Layer 6</text>
<polyline fill="none" stroke="#9aa5b1" points="204.38,-40.5 414.62,-40.5"/>
<text xml:space="preserve" text-anchor="middle" x="309.5" y="-27" font-family="Arial" font-size="10.00" fill="#1f2933">Nginx</text>
<polyline fill="none" stroke="#9aa5b1" points="204.38,-20.5 414.62,-20.5"/>
<text xml:space="preserve" text-anchor="middle" x="309.5" y="-7" font-family="Arial" font-size="10.00" fill="#1f2933">nginx/local.conf · docker&#45;compose.nginx.yml</text>
</g>
<!-- l5&#45;&gt;l6 -->
<g id="edge6" class="edge">
<title>l5&#45;&gt;l6</title>
<path fill="none" stroke="#9aa5b1" d="M309.5,-107.61C309.5,-95.72 309.5,-81.85 309.5,-69.23"/>
<polygon fill="#9aa5b1" stroke="#9aa5b1" points="311.95,-69.24 309.5,-62.24 307.05,-69.24 311.95,-69.24"/>
<text xml:space="preserve" text-anchor="middle" x="329.75" y="-80.95" font-family="Arial" font-size="9.00" fill="#616e7c">if frontend</text>
</g>
<!-- note_req -->
<g id="node8" class="node accent">
<title>note_req</title>
<polygon fill="#d6e4ff" stroke="none" points="233.38,-564 171.62,-564 171.62,-528 233.38,-528 233.38,-564"/>
<text xml:space="preserve" text-anchor="middle" x="202.5" y="-542.7" font-family="Arial" font-size="9.00" fill="#1f2933">every room</text>
</g>
<!-- note_req&#45;&gt;l0 -->
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

@@ -11,7 +11,7 @@
<polygon fill="#0a0a0a" stroke="none" points="-4,4 -4,-607.5 456.62,-607.5 456.62,4 -4,4"/>
<text xml:space="preserve" text-anchor="middle" x="226.31" y="-586.2" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d4a574">Room Layers — init wizard</text>
<!-- l0 -->
<g id="node1" class="node">
<g id="node1" class="node accent">
<title>l0</title>
<polygon fill="#1a1a1a" stroke="#d4a574" points="155.12,-442.5 155.12,-504.75 285.88,-504.75 285.88,-442.5 155.12,-442.5"/>
<text xml:space="preserve" text-anchor="middle" x="220.5" y="-491.25" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e5e5e5">Layer 0</text>
@@ -55,7 +55,7 @@
<text xml:space="preserve" text-anchor="middle" x="191.95" y="-304.95" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#a3a3a3">if managed</text>
</g>
<!-- l4 -->
<g id="node5" class="node">
<g id="node4" class="node">
<title>l4</title>
<polygon fill="#1a1a1a" stroke="#333333" points="229.25,-221.5 229.25,-283.75 441.75,-283.75 441.75,-221.5 229.25,-221.5"/>
<text xml:space="preserve" text-anchor="middle" x="335.5" y="-270.25" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e5e5e5">Layer 4</text>
@@ -65,14 +65,14 @@
<text xml:space="preserve" text-anchor="middle" x="335.5" y="-228.75" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e5e5e5">ctrl/start.sh · stop.sh · status.sh · logs.sh</text>
</g>
<!-- l1&#45;&gt;l4 -->
<g id="edge4" class="edge">
<g id="edge3" class="edge">
<title>l1&#45;&gt;l4</title>
<path fill="none" stroke="#666666" d="M252.84,-331.61C266.06,-319.14 281.48,-304.59 295.41,-291.45"/>
<polygon fill="#666666" stroke="#666666" points="297.43,-294.36 302.3,-284.95 292.63,-289.26 297.43,-294.36"/>
<text xml:space="preserve" text-anchor="middle" x="299.45" y="-304.95" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#a3a3a3">optional</text>
</g>
<!-- l3 -->
<g id="node4" class="node">
<g id="node5" class="node">
<title>l3</title>
<polygon fill="#1a1a1a" stroke="#333333" points="10.88,-111 10.88,-173.25 200.12,-173.25 200.12,-111 10.88,-111"/>
<text xml:space="preserve" text-anchor="middle" x="105.5" y="-159.75" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e5e5e5">Layer 3</text>
@@ -82,7 +82,7 @@
<text xml:space="preserve" text-anchor="middle" x="105.5" y="-118.25" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#e5e5e5">link/main.py · adapters/ · Dockerfile</text>
</g>
<!-- l2&#45;&gt;l3 -->
<g id="edge3" class="edge">
<g id="edge4" class="edge">
<title>l2&#45;&gt;l3</title>
<path fill="none" stroke="#666666" d="M105.5,-221.11C105.5,-209.81 105.5,-196.79 105.5,-184.67"/>
<polygon fill="#666666" stroke="#666666" points="109,-184.92 105.5,-174.92 102,-184.92 109,-184.92"/>
@@ -123,10 +123,10 @@
<text xml:space="preserve" text-anchor="middle" x="359.12" y="-83.95" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#a3a3a3">if frontend</text>
</g>
<!-- note_req -->
<g id="node8" class="node">
<g id="node8" class="node accent">
<title>note_req</title>
<polygon fill="#0a0a0a" stroke="none" points="254,-578.25 187,-578.25 187,-542.25 254,-542.25 254,-578.25"/>
<text xml:space="preserve" text-anchor="middle" x="220.5" y="-557.33" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d4a574">every room</text>
<polygon fill="#1a1a1a" stroke="none" points="254,-578.25 187,-578.25 187,-542.25 254,-542.25 254,-578.25"/>
<text xml:space="preserve" text-anchor="middle" x="220.5" y="-557.33" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#e5e5e5">every room</text>
</g>
<!-- note_req&#45;&gt;l0 -->
</g>

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

@@ -1,88 +1,78 @@
digraph system_overview {
bgcolor="#0a0a0a"
rankdir=TB
compound=true
fontname="Helvetica"
node [fontname="Helvetica" fontsize=11 style=filled color="#333" fontcolor="#e5e5e5"]
edge [fontname="Helvetica" fontsize=9 fontcolor="#a3a3a3" color="#666"]
node [fontname="Helvetica" fontsize=11 style=filled]
edge [fontname="Helvetica" fontsize=9]
label="Soleprint — System Overview"
labelloc=t
fontsize=14
fontcolor="#d4a574"
// Core
subgraph cluster_core {
label="Soleprint Hub"
style=dashed
color="#d4a574"
fontcolor="#d4a574"
class="accent"
hub [label="soleprint\ncore coordinator\nport 12000" fillcolor="#1a1a1a" shape=box]
hub [label="soleprint\ncore coordinator\nport 12000" shape=box]
}
// Artery
subgraph cluster_artery {
label="Artery — Todo lo vital"
style=dashed
color="#b91c1c"
fontcolor="#fca5a5"
class="artery"
veins [label="Veins\nstateless connectors" fillcolor="#1a1a1a"]
shunts [label="Shunts\nmock connectors" fillcolor="#1a1a1a"]
pulses [label="Pulses\ncomposed flows" fillcolor="#1a1a1a"]
veins [label="Veins\nstateless connectors"]
shunts [label="Shunts\nmock connectors"]
pulses [label="Pulses\ncomposed flows"]
}
// Atlas
subgraph cluster_atlas {
label="Atlas — Documentacion accionable"
style=dashed
color="#15803d"
fontcolor="#86efac"
class="atlas"
books [label="Books\ndocumentation" fillcolor="#1a1a1a"]
templates [label="Templates\npatterns" fillcolor="#1a1a1a"]
books [label="Books\ndocumentation"]
templates [label="Templates\npatterns"]
}
// Station
subgraph cluster_station {
label="Station — Centro de control"
style=dashed
color="#1d4ed8"
fontcolor="#93c5fd"
class="station"
tools [label="Tools\ntester · datagen · modelgen" fillcolor="#1a1a1a"]
monitors [label="Monitors\ndatabrowse" fillcolor="#1a1a1a"]
tools [label="Tools\ntester · datagen · modelgen"]
monitors [label="Monitors\ndatabrowse"]
}
// External
subgraph cluster_external {
label="External APIs"
style=dashed
color="#333"
fontcolor="#666"
jira [label="Jira" fillcolor="#1a1a1a" fontcolor="#a3a3a3"]
google [label="Google" fillcolor="#1a1a1a" fontcolor="#a3a3a3"]
slack [label="Slack" fillcolor="#1a1a1a" fontcolor="#a3a3a3"]
jira [label="Jira"]
google [label="Google"]
slack [label="Slack"]
}
// Managed app
subgraph cluster_managed {
label="Managed App"
style=dashed
color="#333"
fontcolor="#666"
app_fe [label="Frontend" fillcolor="#1a1a1a" fontcolor="#a3a3a3"]
app_be [label="Backend" fillcolor="#1a1a1a" fontcolor="#a3a3a3"]
app_db [label="Database" fillcolor="#1a1a1a" fontcolor="#a3a3a3" shape=cylinder]
app_fe [label="Frontend"]
app_be [label="Backend"]
app_db [label="Database" shape=cylinder]
}
// Connections
hub -> veins [label="routes" color="#b91c1c"]
hub -> books [label="routes" color="#15803d"]
hub -> tools [label="routes" color="#1d4ed8"]
hub -> veins [class="artery" label="routes"]
hub -> books [class="atlas" label="routes"]
hub -> tools [class="station" label="routes"]
veins -> jira [label="API"]
veins -> google [label="OAuth"]
@@ -93,5 +83,5 @@ digraph system_overview {
monitors -> app_db [label="browse" style=dashed]
// Sidebar injection
hub -> app_fe [label="sidebar\ninjection" color="#d4a574" style=dashed]
hub -> app_fe [class="accent" label="sidebar\ninjection" style=dashed]
}

View File

@@ -0,0 +1,209 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 14.1.2 (0)
-->
<!-- Title: system_overview Pages: 1 -->
<svg width="1038pt" height="365pt"
viewBox="0.00 0.00 1038.00 365.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 360.75)">
<title>system_overview</title>
<polygon fill="#ffffff" stroke="none" points="-4,4 -4,-360.75 1034,-360.75 1034,4 -4,4"/>
<text xml:space="preserve" text-anchor="middle" x="515" y="-339.45" font-family="Arial" font-size="14.00" fill="#1f2933">Soleprint — System Overview</text>
<g id="clust1" class="cluster accent">
<title>cluster_core</title>
<path fill="#d6e4ff" stroke="#3a7dff" stroke-dasharray="5,2" d="M493,-236.75C493,-236.75 579,-236.75 579,-236.75 585,-236.75 591,-242.75 591,-248.75 591,-248.75 591,-313 591,-313 591,-319 585,-325 579,-325 579,-325 493,-325 493,-325 487,-325 481,-319 481,-313 481,-313 481,-248.75 481,-248.75 481,-242.75 487,-236.75 493,-236.75"/>
<text xml:space="preserve" text-anchor="middle" x="536" y="-307.7" font-family="Arial" font-size="14.00" fill="#616e7c">Soleprint Hub</text>
</g>
<g id="clust2" class="cluster artery">
<title>cluster_artery</title>
<path fill="#fdeaea" stroke="#c0392b" stroke-dasharray="5,2" d="M20,-8C20,-8 328,-8 328,-8 334,-8 340,-14 340,-20 340,-20 340,-196.25 340,-196.25 340,-202.25 334,-208.25 328,-208.25 328,-208.25 20,-208.25 20,-208.25 14,-208.25 8,-202.25 8,-196.25 8,-196.25 8,-20 8,-20 8,-14 14,-8 20,-8"/>
<text xml:space="preserve" text-anchor="middle" x="174" y="-190.95" font-family="Arial" font-size="14.00" fill="#616e7c">Artery — Todo lo vital</text>
</g>
<g id="clust3" class="cluster atlas">
<title>cluster_atlas</title>
<path fill="#e6f5ec" stroke="#1a7f45" stroke-dasharray="5,2" d="M360,-119C360,-119 584,-119 584,-119 590,-119 596,-125 596,-131 596,-131 596,-196.25 596,-196.25 596,-202.25 590,-208.25 584,-208.25 584,-208.25 360,-208.25 360,-208.25 354,-208.25 348,-202.25 348,-196.25 348,-196.25 348,-131 348,-131 348,-125 354,-119 360,-119"/>
<text xml:space="preserve" text-anchor="middle" x="472" y="-190.95" font-family="Arial" font-size="14.00" fill="#616e7c">Atlas — Documentacion accionable</text>
</g>
<g id="clust4" class="cluster station">
<title>cluster_station</title>
<path fill="#e8effd" stroke="#2b5fd9" stroke-dasharray="5,2" d="M616,-119C616,-119 938,-119 938,-119 944,-119 950,-125 950,-131 950,-131 950,-196.25 950,-196.25 950,-202.25 944,-208.25 938,-208.25 938,-208.25 616,-208.25 616,-208.25 610,-208.25 604,-202.25 604,-196.25 604,-196.25 604,-131 604,-131 604,-125 610,-119 616,-119"/>
<text xml:space="preserve" text-anchor="middle" x="777" y="-190.95" font-family="Arial" font-size="14.00" fill="#616e7c">Station — Centro de control</text>
</g>
<g id="clust5" class="cluster">
<title>cluster_external</title>
<path fill="#f5f7fa" stroke="#cbd2d9" stroke-dasharray="5,2" d="M360,-14.75C360,-14.75 558,-14.75 558,-14.75 564,-14.75 570,-20.75 570,-26.75 570,-26.75 570,-78.5 570,-78.5 570,-84.5 564,-90.5 558,-90.5 558,-90.5 360,-90.5 360,-90.5 354,-90.5 348,-84.5 348,-78.5 348,-78.5 348,-26.75 348,-26.75 348,-20.75 354,-14.75 360,-14.75"/>
<text xml:space="preserve" text-anchor="middle" x="459" y="-73.2" font-family="Arial" font-size="14.00" fill="#616e7c">External APIs</text>
</g>
<g id="clust6" class="cluster">
<title>cluster_managed</title>
<path fill="#f5f7fa" stroke="#cbd2d9" stroke-dasharray="5,2" d="M774,-14.75C774,-14.75 1010,-14.75 1010,-14.75 1016,-14.75 1022,-20.75 1022,-26.75 1022,-26.75 1022,-78.5 1022,-78.5 1022,-84.5 1016,-90.5 1010,-90.5 1010,-90.5 774,-90.5 774,-90.5 768,-90.5 762,-84.5 762,-78.5 762,-78.5 762,-26.75 762,-26.75 762,-20.75 768,-14.75 774,-14.75"/>
<text xml:space="preserve" text-anchor="middle" x="892" y="-73.2" font-family="Arial" font-size="14.00" fill="#616e7c">Managed App</text>
</g>
<!-- hub -->
<g id="node1" class="node">
<title>hub</title>
<path fill="#ffffff" stroke="#9aa5b1" d="M571,-293.25C571,-293.25 501,-293.25 501,-293.25 495,-293.25 489,-287.25 489,-281.25 489,-281.25 489,-256.75 489,-256.75 489,-250.75 495,-244.75 501,-244.75 501,-244.75 571,-244.75 571,-244.75 577,-244.75 583,-250.75 583,-256.75 583,-256.75 583,-281.25 583,-281.25 583,-287.25 577,-293.25 571,-293.25"/>
<text xml:space="preserve" text-anchor="middle" x="536" y="-278.8" font-family="Arial" font-size="11.00" fill="#1f2933">soleprint</text>
<text xml:space="preserve" text-anchor="middle" x="536" y="-265.3" font-family="Arial" font-size="11.00" fill="#1f2933">core coordinator</text>
<text xml:space="preserve" text-anchor="middle" x="536" y="-251.8" font-family="Arial" font-size="11.00" fill="#1f2933">port 12000</text>
</g>
<!-- veins -->
<g id="node2" class="node">
<title>veins</title>
<ellipse fill="#ffffff" stroke="#9aa5b1" cx="96" cy="-151.75" rx="80.26" ry="24.75"/>
<text xml:space="preserve" text-anchor="middle" x="96" y="-154.8" font-family="Arial" font-size="11.00" fill="#1f2933">Veins</text>
<text xml:space="preserve" text-anchor="middle" x="96" y="-141.3" font-family="Arial" font-size="11.00" fill="#1f2933">stateless connectors</text>
</g>
<!-- hub&#45;&gt;veins -->
<g id="edge1" class="edge artery">
<title>hub&#45;&gt;veins</title>
<path fill="none" stroke="#c0392b" d="M488.78,-265.75C419.92,-261.21 288.87,-247.71 185,-208.25 166.55,-201.24 147.77,-190.21 132.24,-179.83"/>
<polygon fill="#c0392b" stroke="#c0392b" points="133.76,-177.9 126.6,-175.98 131,-181.95 133.76,-177.9"/>
<text xml:space="preserve" text-anchor="middle" x="252.94" y="-218.2" font-family="Arial" font-size="9.00" fill="#c0392b">routes</text>
</g>
<!-- books -->
<g id="node5" class="node">
<title>books</title>
<ellipse fill="#ffffff" stroke="#9aa5b1" cx="417" cy="-151.75" rx="61.16" ry="24.75"/>
<text xml:space="preserve" text-anchor="middle" x="417" y="-154.8" font-family="Arial" font-size="11.00" fill="#1f2933">Books</text>
<text xml:space="preserve" text-anchor="middle" x="417" y="-141.3" font-family="Arial" font-size="11.00" fill="#1f2933">documentation</text>
</g>
<!-- hub&#45;&gt;books -->
<g id="edge2" class="edge atlas">
<title>hub&#45;&gt;books</title>
<path fill="none" stroke="#1a7f45" d="M511.63,-244.4C492.65,-226.01 466.24,-200.44 446.14,-180.97"/>
<polygon fill="#1a7f45" stroke="#1a7f45" points="447.94,-179.3 441.21,-176.19 444.53,-182.82 447.94,-179.3"/>
<text xml:space="preserve" text-anchor="middle" x="503.41" y="-218.2" font-family="Arial" font-size="9.00" fill="#1a7f45">routes</text>
</g>
<!-- tools -->
<g id="node7" class="node">
<title>tools</title>
<ellipse fill="#ffffff" stroke="#9aa5b1" cx="717" cy="-151.75" rx="105.18" ry="24.75"/>
<text xml:space="preserve" text-anchor="middle" x="717" y="-154.8" font-family="Arial" font-size="11.00" fill="#1f2933">Tools</text>
<text xml:space="preserve" text-anchor="middle" x="717" y="-141.3" font-family="Arial" font-size="11.00" fill="#1f2933">tester · datagen · modelgen</text>
</g>
<!-- hub&#45;&gt;tools -->
<g id="edge3" class="edge station">
<title>hub&#45;&gt;tools</title>
<path fill="none" stroke="#2b5fd9" d="M573.06,-244.4C602.58,-225.6 643.92,-199.28 674.73,-179.67"/>
<polygon fill="#2b5fd9" stroke="#2b5fd9" points="675.66,-181.98 680.25,-176.15 673.03,-177.84 675.66,-181.98"/>
<text xml:space="preserve" text-anchor="middle" x="628.41" y="-218.2" font-family="Arial" font-size="9.00" fill="#2b5fd9">routes</text>
</g>
<!-- app_fe -->
<g id="node12" class="node">
<title>app_fe</title>
<ellipse fill="#ffffff" stroke="#9aa5b1" cx="977" cy="-40.75" rx="37.09" ry="18"/>
<text xml:space="preserve" text-anchor="middle" x="977" y="-37.05" font-family="Arial" font-size="11.00" fill="#1f2933">Frontend</text>
</g>
<!-- hub&#45;&gt;app_fe -->
<g id="edge4" class="edge accent">
<title>hub&#45;&gt;app_fe</title>
<path fill="none" stroke="#3a7dff" stroke-dasharray="5,2" d="M583.13,-268.1C681.31,-267.28 902.87,-259.62 954,-208.25 991.07,-171.01 987.37,-103.78 981.91,-67.18"/>
<polygon fill="#3a7dff" stroke="#3a7dff" points="984.37,-67.01 980.83,-60.49 979.53,-67.79 984.37,-67.01"/>
<text xml:space="preserve" text-anchor="middle" x="1002.46" y="-153.7" font-family="Arial" font-size="9.00" fill="#3a7dff">sidebar</text>
<text xml:space="preserve" text-anchor="middle" x="1002.46" y="-143.2" font-family="Arial" font-size="9.00" fill="#3a7dff">injection</text>
</g>
<!-- pulses -->
<g id="node4" class="node">
<title>pulses</title>
<ellipse fill="#ffffff" stroke="#9aa5b1" cx="96" cy="-40.75" rx="65.94" ry="24.75"/>
<text xml:space="preserve" text-anchor="middle" x="96" y="-43.8" font-family="Arial" font-size="11.00" fill="#1f2933">Pulses</text>
<text xml:space="preserve" text-anchor="middle" x="96" y="-30.3" font-family="Arial" font-size="11.00" fill="#1f2933">composed flows</text>
</g>
<!-- veins&#45;&gt;pulses -->
<g id="edge5" class="edge">
<title>veins&#45;&gt;pulses</title>
<path fill="none" stroke="#9aa5b1" d="M96,-126.56C96,-111.18 96,-91.07 96,-74.3"/>
<polygon fill="#9aa5b1" stroke="#9aa5b1" points="98.45,-74.38 96,-67.39 93.55,-74.39 98.45,-74.38"/>
<text xml:space="preserve" text-anchor="middle" x="114.75" y="-100.45" font-family="Arial" font-size="9.00" fill="#616e7c">compose</text>
</g>
<!-- jira -->
<g id="node9" class="node">
<title>jira</title>
<ellipse fill="#ffffff" stroke="#9aa5b1" cx="383" cy="-40.75" rx="27" ry="18"/>
<text xml:space="preserve" text-anchor="middle" x="383" y="-37.05" font-family="Arial" font-size="11.00" fill="#1f2933">Jira</text>
</g>
<!-- veins&#45;&gt;jira -->
<g id="edge6" class="edge">
<title>veins&#45;&gt;jira</title>
<path fill="none" stroke="#9aa5b1" d="M144.51,-131.81C157.52,-127.18 171.68,-122.54 185,-119 254.38,-100.54 282.3,-127.21 344,-90.5 354.39,-84.32 363.13,-74.26 369.69,-64.89"/>
<polygon fill="#9aa5b1" stroke="#9aa5b1" points="371.64,-66.39 373.44,-59.19 367.54,-63.69 371.64,-66.39"/>
<text xml:space="preserve" text-anchor="middle" x="333.55" y="-100.45" font-family="Arial" font-size="9.00" fill="#616e7c">API</text>
</g>
<!-- google -->
<g id="node10" class="node">
<title>google</title>
<ellipse fill="#ffffff" stroke="#9aa5b1" cx="459" cy="-40.75" rx="31.48" ry="18"/>
<text xml:space="preserve" text-anchor="middle" x="459" y="-37.05" font-family="Arial" font-size="11.00" fill="#1f2933">Google</text>
</g>
<!-- veins&#45;&gt;google -->
<g id="edge7" class="edge">
<title>veins&#45;&gt;google</title>
<path fill="none" stroke="#9aa5b1" d="M142.87,-131.26C156.26,-126.46 171.02,-121.87 185,-119 287.63,-97.92 330.48,-146.53 419,-90.5 429.17,-84.06 437.99,-74.16 444.73,-64.95"/>
<polygon fill="#9aa5b1" stroke="#9aa5b1" points="446.63,-66.51 448.61,-59.37 442.61,-63.72 446.63,-66.51"/>
<text xml:space="preserve" text-anchor="middle" x="416.29" y="-100.45" font-family="Arial" font-size="9.00" fill="#616e7c">OAuth</text>
</g>
<!-- slack -->
<g id="node11" class="node">
<title>slack</title>
<ellipse fill="#ffffff" stroke="#9aa5b1" cx="535" cy="-40.75" rx="27" ry="18"/>
<text xml:space="preserve" text-anchor="middle" x="535" y="-37.05" font-family="Arial" font-size="11.00" fill="#1f2933">Slack</text>
</g>
<!-- veins&#45;&gt;slack -->
<g id="edge8" class="edge">
<title>veins&#45;&gt;slack</title>
<path fill="none" stroke="#9aa5b1" d="M142.37,-131.22C155.88,-126.36 170.83,-121.75 185,-119 293.3,-98.01 324.04,-126.22 433,-109 463.09,-104.24 473.93,-107.8 499,-90.5 508.43,-83.99 516.35,-74.2 522.34,-65.1"/>
<polygon fill="#9aa5b1" stroke="#9aa5b1" points="524.32,-66.55 525.93,-59.3 520.16,-63.97 524.32,-66.55"/>
<text xml:space="preserve" text-anchor="middle" x="492.52" y="-100.45" font-family="Arial" font-size="9.00" fill="#616e7c">API</text>
</g>
<!-- shunts -->
<g id="node3" class="node">
<title>shunts</title>
<ellipse fill="#ffffff" stroke="#9aa5b1" cx="263" cy="-151.75" rx="68.59" ry="24.75"/>
<text xml:space="preserve" text-anchor="middle" x="263" y="-154.8" font-family="Arial" font-size="11.00" fill="#1f2933">Shunts</text>
<text xml:space="preserve" text-anchor="middle" x="263" y="-141.3" font-family="Arial" font-size="11.00" fill="#1f2933">mock connectors</text>
</g>
<!-- templates -->
<g id="node6" class="node">
<title>templates</title>
<ellipse fill="#ffffff" stroke="#9aa5b1" cx="542" cy="-151.75" rx="45.79" ry="24.75"/>
<text xml:space="preserve" text-anchor="middle" x="542" y="-154.8" font-family="Arial" font-size="11.00" fill="#1f2933">Templates</text>
<text xml:space="preserve" text-anchor="middle" x="542" y="-141.3" font-family="Arial" font-size="11.00" fill="#1f2933">patterns</text>
</g>
<!-- app_be -->
<g id="node13" class="node">
<title>app_be</title>
<ellipse fill="#ffffff" stroke="#9aa5b1" cx="806" cy="-40.75" rx="36.16" ry="18"/>
<text xml:space="preserve" text-anchor="middle" x="806" y="-37.05" font-family="Arial" font-size="11.00" fill="#1f2933">Backend</text>
</g>
<!-- tools&#45;&gt;app_be -->
<g id="edge9" class="edge">
<title>tools&#45;&gt;app_be</title>
<path fill="none" stroke="#9aa5b1" stroke-dasharray="5,2" d="M736.31,-127.1C751.52,-108.47 772.58,-82.68 787.56,-64.33"/>
<polygon fill="#9aa5b1" stroke="#9aa5b1" points="789.29,-66.08 791.82,-59.11 785.5,-62.99 789.29,-66.08"/>
<text xml:space="preserve" text-anchor="middle" x="765.96" y="-100.45" font-family="Arial" font-size="9.00" fill="#616e7c">test</text>
</g>
<!-- monitors -->
<g id="node8" class="node">
<title>monitors</title>
<ellipse fill="#ffffff" stroke="#9aa5b1" cx="891" cy="-151.75" rx="51.09" ry="24.75"/>
<text xml:space="preserve" text-anchor="middle" x="891" y="-154.8" font-family="Arial" font-size="11.00" fill="#1f2933">Monitors</text>
<text xml:space="preserve" text-anchor="middle" x="891" y="-141.3" font-family="Arial" font-size="11.00" fill="#1f2933">databrowse</text>
</g>
<!-- app_db -->
<g id="node14" class="node">
<title>app_db</title>
<path fill="#ffffff" stroke="#9aa5b1" d="M922.25,-55.48C922.25,-57.28 908.24,-58.75 891,-58.75 873.76,-58.75 859.75,-57.28 859.75,-55.48 859.75,-55.48 859.75,-26.02 859.75,-26.02 859.75,-24.22 873.76,-22.75 891,-22.75 908.24,-22.75 922.25,-24.22 922.25,-26.02 922.25,-26.02 922.25,-55.48 922.25,-55.48"/>
<path fill="none" stroke="#9aa5b1" d="M922.25,-55.48C922.25,-53.67 908.24,-52.2 891,-52.2 873.76,-52.2 859.75,-53.67 859.75,-55.48"/>
<text xml:space="preserve" text-anchor="middle" x="891" y="-37.05" font-family="Arial" font-size="11.00" fill="#1f2933">Database</text>
</g>
<!-- monitors&#45;&gt;app_db -->
<g id="edge10" class="edge">
<title>monitors&#45;&gt;app_db</title>
<path fill="none" stroke="#9aa5b1" stroke-dasharray="5,2" d="M891,-126.56C891,-108.99 891,-85.25 891,-67.36"/>
<polygon fill="#9aa5b1" stroke="#9aa5b1" points="893.45,-67.61 891,-60.61 888.55,-67.61 893.45,-67.61"/>
<text xml:space="preserve" text-anchor="middle" x="906" y="-100.45" font-family="Arial" font-size="9.00" fill="#616e7c">browse</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -10,22 +10,22 @@
<title>system_overview</title>
<polygon fill="#0a0a0a" stroke="none" points="-4,4 -4,-368.25 1149,-368.25 1149,4 -4,4"/>
<text xml:space="preserve" text-anchor="middle" x="572.5" y="-346.95" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d4a574">Soleprint — System Overview</text>
<g id="clust1" class="cluster">
<g id="clust1" class="cluster accent">
<title>cluster_core</title>
<polygon fill="#0a0a0a" stroke="#d4a574" stroke-dasharray="5,2" points="538,-241.25 538,-331 660,-331 660,-241.25 538,-241.25"/>
<text xml:space="preserve" text-anchor="middle" x="599" y="-313.7" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d4a574">Soleprint Hub</text>
</g>
<g id="clust2" class="cluster">
<g id="clust2" class="cluster artery">
<title>cluster_artery</title>
<polygon fill="#0a0a0a" stroke="#b91c1c" stroke-dasharray="5,2" points="8,-8 8,-212 382,-212 382,-8 8,-8"/>
<text xml:space="preserve" text-anchor="middle" x="195" y="-194.7" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#fca5a5">Artery — Todo lo vital</text>
</g>
<g id="clust3" class="cluster">
<g id="clust3" class="cluster atlas">
<title>cluster_atlas</title>
<polygon fill="#0a0a0a" stroke="#15803d" stroke-dasharray="5,2" points="390,-121.25 390,-212 665,-212 665,-121.25 390,-121.25"/>
<text xml:space="preserve" text-anchor="middle" x="527.5" y="-194.7" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#86efac">Atlas — Documentacion accionable</text>
</g>
<g id="clust4" class="cluster">
<g id="clust4" class="cluster station">
<title>cluster_station</title>
<polygon fill="#0a0a0a" stroke="#1d4ed8" stroke-dasharray="5,2" points="673,-121.25 673,-212 1062,-212 1062,-121.25 673,-121.25"/>
<text xml:space="preserve" text-anchor="middle" x="867.5" y="-194.7" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#93c5fd">Station — Centro de control</text>
@@ -56,11 +56,11 @@
<text xml:space="preserve" text-anchor="middle" x="108" y="-143.55" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">stateless connectors</text>
</g>
<!-- hub&#45;&gt;veins -->
<g id="edge1" class="edge">
<g id="edge1" class="edge artery">
<title>hub&#45;&gt;veins</title>
<path fill="none" stroke="#b91c1c" d="M545.18,-270.24C468.57,-265.68 324.55,-252.07 209,-212 188.62,-204.93 167.6,-193.79 150.05,-183.23"/>
<polygon fill="#b91c1c" stroke="#b91c1c" points="152.32,-180.52 141.98,-178.24 148.65,-186.47 152.32,-180.52"/>
<text xml:space="preserve" text-anchor="middle" x="284.95" y="-222.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#a3a3a3">routes</text>
<text xml:space="preserve" text-anchor="middle" x="284.95" y="-222.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#fca5a5">routes</text>
</g>
<!-- books -->
<g id="node5" class="node">
@@ -70,11 +70,11 @@
<text xml:space="preserve" text-anchor="middle" x="468" y="-143.55" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">documentation</text>
</g>
<!-- hub&#45;&gt;books -->
<g id="edge2" class="edge">
<g id="edge2" class="edge atlas">
<title>hub&#45;&gt;books</title>
<path fill="none" stroke="#15803d" d="M572.81,-249C552.24,-230.55 523.47,-204.75 501.27,-184.84"/>
<polygon fill="#15803d" stroke="#15803d" points="503.73,-182.34 493.94,-178.27 499.05,-187.55 503.73,-182.34"/>
<text xml:space="preserve" text-anchor="middle" x="567.14" y="-222.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#a3a3a3">routes</text>
<text xml:space="preserve" text-anchor="middle" x="567.14" y="-222.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#86efac">routes</text>
</g>
<!-- tools -->
<g id="node7" class="node">
@@ -84,25 +84,25 @@
<text xml:space="preserve" text-anchor="middle" x="802" y="-143.55" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">tester · datagen · modelgen</text>
</g>
<!-- hub&#45;&gt;tools -->
<g id="edge3" class="edge">
<g id="edge3" class="edge station">
<title>hub&#45;&gt;tools</title>
<path fill="none" stroke="#1d4ed8" d="M639.59,-249C672.13,-230.17 717.93,-203.66 752.6,-183.59"/>
<polygon fill="#1d4ed8" stroke="#1d4ed8" points="754.3,-186.65 761.2,-178.61 750.8,-180.59 754.3,-186.65"/>
<text xml:space="preserve" text-anchor="middle" x="702.6" y="-222.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#a3a3a3">routes</text>
<text xml:space="preserve" text-anchor="middle" x="702.6" y="-222.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#93c5fd">routes</text>
</g>
<!-- app_fe -->
<g id="node12" class="node">
<title>app_fe</title>
<ellipse fill="#1a1a1a" stroke="#333333" cx="1089" cy="-40.75" rx="39.9" ry="18"/>
<text xml:space="preserve" text-anchor="middle" x="1089" y="-37.05" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#a3a3a3">Frontend</text>
<text xml:space="preserve" text-anchor="middle" x="1089" y="-37.05" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">Frontend</text>
</g>
<!-- hub&#45;&gt;app_fe -->
<g id="edge10" class="edge">
<g id="edge4" class="edge accent">
<title>hub&#45;&gt;app_fe</title>
<path fill="none" stroke="#d4a574" stroke-dasharray="5,2" d="M652.46,-273.38C762.51,-274.01 1008.83,-268.85 1066,-212 1103.21,-174.99 1100.13,-108.42 1094.57,-70.42"/>
<polygon fill="#d4a574" stroke="#d4a574" points="1098.04,-69.95 1092.97,-60.65 1091.14,-71.08 1098.04,-69.95"/>
<text xml:space="preserve" text-anchor="middle" x="1116.94" y="-156.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#a3a3a3">sidebar</text>
<text xml:space="preserve" text-anchor="middle" x="1116.94" y="-145.45" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#a3a3a3">injection</text>
<text xml:space="preserve" text-anchor="middle" x="1116.94" y="-156.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d4a574">sidebar</text>
<text xml:space="preserve" text-anchor="middle" x="1116.94" y="-145.45" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d4a574">injection</text>
</g>
<!-- pulses -->
<g id="node4" class="node">
@@ -112,7 +112,7 @@
<text xml:space="preserve" text-anchor="middle" x="108" y="-30.3" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">composed flows</text>
</g>
<!-- veins&#45;&gt;pulses -->
<g id="edge7" class="edge">
<g id="edge5" class="edge">
<title>veins&#45;&gt;pulses</title>
<path fill="none" stroke="#666666" d="M108,-128.86C108,-113.7 108,-93.88 108,-76.98"/>
<polygon fill="#666666" stroke="#666666" points="111.5,-77.35 108,-67.35 104.5,-77.35 111.5,-77.35"/>
@@ -122,10 +122,10 @@
<g id="node9" class="node">
<title>jira</title>
<ellipse fill="#1a1a1a" stroke="#333333" cx="425" cy="-40.75" rx="27" ry="18"/>
<text xml:space="preserve" text-anchor="middle" x="425" y="-37.05" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#a3a3a3">Jira</text>
<text xml:space="preserve" text-anchor="middle" x="425" y="-37.05" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">Jira</text>
</g>
<!-- veins&#45;&gt;jira -->
<g id="edge4" class="edge">
<g id="edge6" class="edge">
<title>veins&#45;&gt;jira</title>
<path fill="none" stroke="#666666" d="M163.97,-133.86C178.52,-129.32 194.25,-124.77 209,-121.25 286.56,-102.75 317.5,-132.81 386,-92 395.89,-86.1 404.2,-76.67 410.57,-67.57"/>
<polygon fill="#666666" stroke="#666666" points="413.44,-69.58 415.88,-59.27 407.54,-65.8 413.44,-69.58"/>
@@ -135,10 +135,10 @@
<g id="node10" class="node">
<title>google</title>
<ellipse fill="#1a1a1a" stroke="#333333" cx="504" cy="-40.75" rx="33.82" ry="18"/>
<text xml:space="preserve" text-anchor="middle" x="504" y="-37.05" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#a3a3a3">Google</text>
<text xml:space="preserve" text-anchor="middle" x="504" y="-37.05" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">Google</text>
</g>
<!-- veins&#45;&gt;google -->
<g id="edge5" class="edge">
<g id="edge7" class="edge">
<title>veins&#45;&gt;google</title>
<path fill="none" stroke="#666666" d="M161.63,-133.42C176.76,-128.65 193.36,-124.1 209,-121.25 287.39,-106.97 309.3,-123.69 388,-111.25 421.14,-106.01 432.58,-109.83 461,-92 471.11,-85.66 480.09,-76.19 487.2,-67.17"/>
<polygon fill="#666666" stroke="#666666" points="489.88,-69.43 493,-59.31 484.25,-65.28 489.88,-69.43"/>
@@ -148,10 +148,10 @@
<g id="node11" class="node">
<title>slack</title>
<ellipse fill="#1a1a1a" stroke="#333333" cx="584" cy="-40.75" rx="27.74" ry="18"/>
<text xml:space="preserve" text-anchor="middle" x="584" y="-37.05" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#a3a3a3">Slack</text>
<text xml:space="preserve" text-anchor="middle" x="584" y="-37.05" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">Slack</text>
</g>
<!-- veins&#45;&gt;slack -->
<g id="edge6" class="edge">
<g id="edge8" class="edge">
<title>veins&#45;&gt;slack</title>
<path fill="none" stroke="#666666" d="M161.07,-133.39C176.34,-128.56 193.15,-123.98 209,-121.25 326.9,-100.91 359.79,-129.67 478,-111.25 509.46,-106.35 520.8,-110.08 547,-92 556.07,-85.74 563.77,-76.52 569.76,-67.68"/>
<polygon fill="#666666" stroke="#666666" points="572.65,-69.66 574.98,-59.33 566.72,-65.96 572.65,-69.66"/>
@@ -175,10 +175,10 @@
<g id="node13" class="node">
<title>app_be</title>
<ellipse fill="#1a1a1a" stroke="#333333" cx="906" cy="-40.75" rx="38.96" ry="18"/>
<text xml:space="preserve" text-anchor="middle" x="906" y="-37.05" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#a3a3a3">Backend</text>
<text xml:space="preserve" text-anchor="middle" x="906" y="-37.05" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">Backend</text>
</g>
<!-- tools&#45;&gt;app_be -->
<g id="edge8" class="edge">
<g id="edge9" class="edge">
<title>tools&#45;&gt;app_be</title>
<path fill="none" stroke="#666666" stroke-dasharray="5,2" d="M824.06,-129.4C841.45,-110.79 865.64,-84.93 883.22,-66.12"/>
<polygon fill="#666666" stroke="#666666" points="885.72,-68.57 889.99,-58.88 880.6,-63.79 885.72,-68.57"/>
@@ -196,10 +196,10 @@
<title>app_db</title>
<path fill="#1a1a1a" stroke="#333333" d="M1031.25,-55.48C1031.25,-57.28 1015.9,-58.75 997,-58.75 978.1,-58.75 962.75,-57.28 962.75,-55.48 962.75,-55.48 962.75,-26.02 962.75,-26.02 962.75,-24.22 978.1,-22.75 997,-22.75 1015.9,-22.75 1031.25,-24.22 1031.25,-26.02 1031.25,-26.02 1031.25,-55.48 1031.25,-55.48"/>
<path fill="none" stroke="#333333" d="M1031.25,-55.48C1031.25,-53.67 1015.9,-52.2 997,-52.2 978.1,-52.2 962.75,-53.67 962.75,-55.48"/>
<text xml:space="preserve" text-anchor="middle" x="997" y="-37.05" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#a3a3a3">Database</text>
<text xml:space="preserve" text-anchor="middle" x="997" y="-37.05" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">Database</text>
</g>
<!-- monitors&#45;&gt;app_db -->
<g id="edge9" class="edge">
<g id="edge10" class="edge">
<title>monitors&#45;&gt;app_db</title>
<path fill="none" stroke="#666666" stroke-dasharray="5,2" d="M997,-128.86C997,-111.64 997,-88.42 997,-70.29"/>
<polygon fill="#666666" stroke="#666666" points="1000.5,-70.46 997,-60.46 993.5,-70.46 1000.5,-70.46"/>

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,92 @@
// Dark — the palette the docs have always used, restated as a theme.
//
// This exists so the .dot sources can stop carrying colour. It reproduces what
// the committed SVGs look like today, which also makes it the regression
// oracle: strip the inline colours from a source, render it through this, and
// the result should be the diagram you had before. If it is not, the strip was
// wrong.
//
// Palette matches common/theme/themes/soleprint.css, so a diagram and the page
// around it are the same visual language.
BEGIN {
string BG = "#0a0a0a";
string FILL = "#1a1a1a";
string LINE = "#333333";
string INK = "#e5e5e5";
string MUTED = "#a3a3a3";
string DIM = "#666666";
string FONT = "Helvetica";
graph_t sg;
// The soleprint amber, and the three system colours, each with the lighter
// text variant the docs use for labels on dark.
string ACC_S = "#d4a574"; string ACC_T = "#d4a574";
string ART_S = "#b91c1c"; string ART_T = "#fca5a5";
string ATL_S = "#15803d"; string ATL_T = "#86efac";
string STA_S = "#1d4ed8"; string STA_T = "#93c5fd";
}
BEG_G {
$G.bgcolor = BG;
setDflt($G, "N", "fontname", FONT);
setDflt($G, "E", "fontname", FONT);
// Declare `class` so reading it on an untagged object is defined
// rather than a warning — most nodes carry no class by design.
setDflt($G, "G", "class", "");
setDflt($G, "N", "class", "");
setDflt($G, "E", "class", "");
$G.fontname = FONT;
// The graph title takes the accent of whatever system the graph is about;
// graphs declare that with a class on the digraph itself.
if ($G.class == "artery") { $G.fontcolor = ART_T; }
else if ($G.class == "atlas") { $G.fontcolor = ATL_T; }
else if ($G.class == "station") { $G.fontcolor = STA_T; }
else { $G.fontcolor = ACC_T; }
for (sg = fstsubg($G); sg; sg = nxtsubg(sg)) {
if (index(sg.name, "cluster") == 0) {
sg.fontname = FONT;
sg.color = LINE;
sg.fontcolor = DIM;
if (sg.class == "artery") { sg.color = ART_S; sg.fontcolor = ART_T; }
else if (sg.class == "atlas") { sg.color = ATL_S; sg.fontcolor = ATL_T; }
else if (sg.class == "station") { sg.color = STA_S; sg.fontcolor = STA_T; }
else if (sg.class == "accent") { sg.color = ACC_S; sg.fontcolor = ACC_T; }
else if (sg.class == "ok") { sg.color = ATL_S; sg.fontcolor = ATL_T; }
if (index(sg.style, "dashed") < 0) { sg.style = "dashed"; }
}
}
}
N {
if (index($.style, "invis") >= 0) { continue; }
$.fontname = FONT;
$.fontcolor = INK;
$.color = LINE;
$.fillcolor = FILL;
if ($.class == "accent") { $.color = ACC_S; }
else if ($.class == "artery") { $.color = ART_S; }
else if ($.class == "atlas") { $.color = ATL_S; }
else if ($.class == "station") { $.color = STA_S; }
else if ($.class == "accent-text") { $.fontcolor = ACC_T; }
else if ($.class == "ok") { $.fontcolor = ATL_T; }
else if ($.class == "muted") { $.fontcolor = MUTED; }
if (index($.style, "dashed") >= 0) { $.style = "filled,dashed"; }
else { $.style = "filled"; }
}
E {
$.fontname = FONT;
$.fontcolor = MUTED;
$.color = DIM;
if ($.class == "accent") { $.color = ACC_S; $.fontcolor = ACC_T; }
else if ($.class == "artery") { $.color = ART_S; $.fontcolor = ART_T; }
else if ($.class == "atlas") { $.color = ATL_S; $.fontcolor = ATL_T; }
else if ($.class == "station") { $.color = STA_S; $.fontcolor = STA_T; }
}

View File

@@ -0,0 +1,114 @@
// Lucid — graphviz output shaped after a lucid.app export.
//
// Run through gvpr, not as .dot attributes, because gvpr rewrites the parsed
// graph: it wins over whatever the source set inline, so one .dot renders in
// any theme without being edited. See render.sh.
//
// The look, and why each part is what it is:
// white canvas this ends up in a document and gets printed
// rounded box, hairline Lucid's default shape is a rounded rect with a grey
// 1px stroke, not a coloured fill
// pale-blue accent #d6e4ff on #3a7dff is Lucid's own selected-shape pair
// Arial the one face guaranteed on Windows, and aliased to
// Liberation Sans by fontconfig on Linux — so the SVG
// measures the same on both and the text does not
// reflow out of its box
//
// Structure is preserved, only palette is replaced. Two styles carry meaning
// rather than decoration and are explicitly kept: `invis` (a layout spacer —
// overwriting it makes hidden scaffolding visible) and `dashed` (a weaker
// relationship). Shapes are never touched: a cylinder is a datastore.
BEGIN {
string BG = "#ffffff";
string INK = "#1f2933";
string MUTED = "#616e7c";
string LINE = "#9aa5b1";
string SOFT = "#cbd2d9";
string FONT = "Arial";
graph_t sg;
string FILL = "#ffffff";
string FILL_ALT = "#f5f7fa";
// class -> (stroke, fill). Lucid's palette for emphasised shapes.
string ACC_S = "#3a7dff"; string ACC_F = "#d6e4ff";
string ART_S = "#c0392b"; string ART_F = "#fdeaea";
string ATL_S = "#1a7f45"; string ATL_F = "#e6f5ec";
string STA_S = "#2b5fd9"; string STA_F = "#e8effd";
}
BEG_G {
$G.bgcolor = BG;
$G.fontname = FONT;
$G.fontcolor = INK;
// Defaults for anything the per-node block does not reach.
setDflt($G, "N", "fontname", FONT);
setDflt($G, "E", "fontname", FONT);
// Declare `class` so reading it on an untagged object is defined
// rather than a warning — most nodes carry no class by design.
setDflt($G, "G", "class", "");
setDflt($G, "N", "class", "");
setDflt($G, "E", "class", "");
// Clusters: a pale container, the way Lucid draws a grouping box.
for (sg = fstsubg($G); sg; sg = nxtsubg(sg)) {
if (index(sg.name, "cluster") == 0) {
sg.fontname = FONT;
sg.color = SOFT;
sg.fontcolor = MUTED;
sg.bgcolor = FILL_ALT;
// A tagged container keeps its identity, the way a Lucid swimlane
// is tinted by what it holds.
if (sg.class == "artery") { sg.color = ART_S; sg.bgcolor = ART_F; }
else if (sg.class == "atlas") { sg.color = ATL_S; sg.bgcolor = ATL_F; }
else if (sg.class == "station") { sg.color = STA_S; sg.bgcolor = STA_F; }
else if (sg.class == "accent") { sg.color = ACC_S; sg.bgcolor = ACC_F; }
// Keep dashed where the source chose it; it reads as "logical
// grouping" rather than "deployed boundary".
if (index(sg.style, "dashed") < 0) { sg.style = "rounded"; }
else { sg.style = "dashed,rounded"; }
}
}
}
N {
// An invisible node is layout scaffolding. Filling it would draw it.
if (index($.style, "invis") >= 0) { continue; }
$.fontname = FONT;
$.fontcolor = INK;
$.color = LINE;
$.fillcolor = FILL;
$.penwidth = 1;
if ($.class == "accent") { $.color = ACC_S; $.fillcolor = ACC_F; }
else if ($.class == "artery") { $.color = ART_S; $.fillcolor = ART_F; }
else if ($.class == "atlas") { $.color = ATL_S; $.fillcolor = ATL_F; }
else if ($.class == "station") { $.color = STA_S; $.fillcolor = STA_F; }
else if ($.class == "accent-text") { $.fontcolor = ACC_S; }
else if ($.class == "ok") { $.color = ATL_S; $.fillcolor = ATL_F; }
else if ($.class == "muted") { $.fillcolor = FILL_ALT; $.fontcolor = MUTED; }
// `record` ignores rounding, and plaintext has no box to round.
if ($.shape == "record" || $.shape == "Mrecord" || $.shape == "plaintext") {
$.style = "filled";
} else if (index($.style, "dashed") >= 0) {
$.style = "filled,rounded,dashed";
} else {
$.style = "filled,rounded";
}
}
E {
$.fontname = FONT;
$.fontcolor = MUTED;
$.color = LINE;
$.arrowsize = 0.7;
if ($.class == "accent") { $.color = ACC_S; $.fontcolor = ACC_S; }
else if ($.class == "artery") { $.color = ART_S; $.fontcolor = ART_S; }
else if ($.class == "atlas") { $.color = ATL_S; $.fontcolor = ATL_S; }
else if ($.class == "station") { $.color = STA_S; $.fontcolor = STA_S; }
}

View File

@@ -1,45 +1,39 @@
digraph wrapping {
bgcolor="#0a0a0a"
rankdir=LR
fontname="Helvetica"
node [fontname="Helvetica" fontsize=11 style=filled color="#333" fontcolor="#e5e5e5" shape=box]
edge [fontname="Helvetica" fontsize=9 fontcolor="#a3a3a3" color="#666"]
node [fontname="Helvetica" fontsize=11 style=filled shape=box]
edge [fontname="Helvetica" fontsize=9]
label="Sidebar Injection — How Wrapping Works"
labelloc=t
fontsize=14
fontcolor="#d4a574"
browser [label="Browser" fillcolor="#1a1a1a" shape=oval]
browser [label="Browser" shape=oval]
subgraph cluster_nginx {
label="Nginx (reverse proxy)"
style=dashed
color="#d4a574"
fontcolor="#d4a574"
class="accent"
proxy [label="proxy_pass\n+\nsub_filter\ninjects sidebar" fillcolor="#1a1a1a" shape=component]
proxy [label="proxy_pass\n+\nsub_filter\ninjects sidebar" shape=component]
}
subgraph cluster_app {
label="Managed App"
style=dashed
color="#333"
fontcolor="#666"
frontend [label="Frontend\n(React/Next/Vue)" fillcolor="#1a1a1a"]
backend [label="Backend API" fillcolor="#1a1a1a"]
frontend [label="Frontend\n(React/Next/Vue)"]
backend [label="Backend API"]
}
subgraph cluster_spr {
label="Soleprint"
style=dashed
color="#d4a574"
fontcolor="#d4a574"
class="accent"
sidebar_css [label="sidebar.css" fillcolor="#1a1a1a"]
sidebar_js [label="sidebar.js" fillcolor="#1a1a1a"]
hub [label="Hub API\n/api/sidebar/config" fillcolor="#1a1a1a"]
sidebar_css [label="sidebar.css"]
sidebar_js [label="sidebar.js"]
hub [label="Hub API\n/api/sidebar/config"]
}
browser -> proxy [label="myroom.spr.local.ar"]
@@ -47,8 +41,8 @@ digraph wrapping {
proxy -> hub [label="/spr/ → soleprint"]
// The injection
proxy -> sidebar_css [label="injects into </head>" color="#d4a574" style=dashed]
proxy -> sidebar_js [color="#d4a574" style=dashed]
proxy -> sidebar_css [class="accent" label="injects into </head>" style=dashed]
proxy -> sidebar_js [class="accent" style=dashed]
sidebar_js -> hub [label="loads config" color="#d4a574"]
sidebar_js -> hub [class="accent" label="loads config"]
}

View File

@@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 14.1.2 (0)
-->
<!-- Title: wrapping Pages: 1 -->
<svg width="742pt" height="325pt"
viewBox="0.00 0.00 742.00 325.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 321.19)">
<title>wrapping</title>
<polygon fill="#ffffff" stroke="none" points="-4,4 -4,-321.19 738.19,-321.19 738.19,4 -4,4"/>
<text xml:space="preserve" text-anchor="middle" x="367.1" y="-299.89" font-family="Arial" font-size="14.00" fill="#1f2933">Sidebar Injection — How Wrapping Works</text>
<g id="clust1" class="cluster accent">
<title>cluster_nginx</title>
<path fill="#d6e4ff" stroke="#3a7dff" stroke-dasharray="5,2" d="M189.94,-24.44C189.94,-24.44 310.94,-24.44 310.94,-24.44 316.94,-24.44 322.94,-30.44 322.94,-36.44 322.94,-36.44 322.94,-114.44 322.94,-114.44 322.94,-120.44 316.94,-126.44 310.94,-126.44 310.94,-126.44 189.94,-126.44 189.94,-126.44 183.94,-126.44 177.94,-120.44 177.94,-114.44 177.94,-114.44 177.94,-36.44 177.94,-36.44 177.94,-30.44 183.94,-24.44 189.94,-24.44"/>
<text xml:space="preserve" text-anchor="middle" x="250.44" y="-109.14" font-family="Arial" font-size="14.00" fill="#616e7c">Nginx (reverse proxy)</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_app</title>
<path fill="#f5f7fa" stroke="#cbd2d9" stroke-dasharray="5,2" d="M434.44,-155.44C434.44,-155.44 525.69,-155.44 525.69,-155.44 531.69,-155.44 537.69,-161.44 537.69,-167.44 537.69,-167.44 537.69,-273.44 537.69,-273.44 537.69,-279.44 531.69,-285.44 525.69,-285.44 525.69,-285.44 434.44,-285.44 434.44,-285.44 428.44,-285.44 422.44,-279.44 422.44,-273.44 422.44,-273.44 422.44,-167.44 422.44,-167.44 422.44,-161.44 428.44,-155.44 434.44,-155.44"/>
<text xml:space="preserve" text-anchor="middle" x="480.07" y="-268.14" font-family="Arial" font-size="14.00" fill="#616e7c">Managed App</text>
</g>
<g id="clust3" class="cluster accent">
<title>cluster_spr</title>
<path fill="#d6e4ff" stroke="#3a7dff" stroke-dasharray="5,2" d="M449.44,-17.44C449.44,-17.44 714.19,-17.44 714.19,-17.44 720.19,-17.44 726.19,-23.44 726.19,-29.44 726.19,-29.44 726.19,-135.44 726.19,-135.44 726.19,-141.44 720.19,-147.44 714.19,-147.44 714.19,-147.44 449.44,-147.44 449.44,-147.44 443.44,-147.44 437.44,-141.44 437.44,-135.44 437.44,-135.44 437.44,-29.44 437.44,-29.44 437.44,-23.44 443.44,-17.44 449.44,-17.44"/>
<text xml:space="preserve" text-anchor="middle" x="581.82" y="-130.14" font-family="Arial" font-size="14.00" fill="#616e7c">Soleprint</text>
</g>
<!-- proxy -->
<g id="node1" class="node">
<title>proxy</title>
<polygon fill="#ffffff" stroke="#9aa5b1" points="292.07,-94.44 207.82,-94.44 207.82,-90.44 203.82,-90.44 203.82,-86.44 207.82,-86.44 207.82,-40.44 203.82,-40.44 203.82,-36.44 207.82,-36.44 207.82,-32.44 292.07,-32.44 292.07,-94.44"/>
<polyline fill="none" stroke="#9aa5b1" points="207.82,-90.44 211.82,-90.44 211.82,-86.44 207.82,-86.44"/>
<polyline fill="none" stroke="#9aa5b1" points="207.82,-40.44 211.82,-40.44 211.82,-36.44 207.82,-36.44"/>
<text xml:space="preserve" text-anchor="middle" x="249.94" y="-79.99" font-family="Arial" font-size="11.00" fill="#1f2933">proxy_pass</text>
<text xml:space="preserve" text-anchor="middle" x="249.94" y="-66.49" font-family="Arial" font-size="11.00" fill="#1f2933">+</text>
<text xml:space="preserve" text-anchor="middle" x="249.94" y="-52.99" font-family="Arial" font-size="11.00" fill="#1f2933">sub_filter</text>
<text xml:space="preserve" text-anchor="middle" x="249.94" y="-39.49" font-family="Arial" font-size="11.00" fill="#1f2933">injects sidebar</text>
</g>
<!-- frontend -->
<g id="node2" class="node">
<title>frontend</title>
<path fill="#ffffff" stroke="#9aa5b1" d="M517.69,-199.44C517.69,-199.44 442.44,-199.44 442.44,-199.44 436.44,-199.44 430.44,-193.44 430.44,-187.44 430.44,-187.44 430.44,-175.44 430.44,-175.44 430.44,-169.44 436.44,-163.44 442.44,-163.44 442.44,-163.44 517.69,-163.44 517.69,-163.44 523.69,-163.44 529.69,-169.44 529.69,-175.44 529.69,-175.44 529.69,-187.44 529.69,-187.44 529.69,-193.44 523.69,-199.44 517.69,-199.44"/>
<text xml:space="preserve" text-anchor="middle" x="480.07" y="-184.49" font-family="Arial" font-size="11.00" fill="#1f2933">Frontend</text>
<text xml:space="preserve" text-anchor="middle" x="480.07" y="-170.99" font-family="Arial" font-size="11.00" fill="#1f2933">(React/Next/Vue)</text>
</g>
<!-- proxy&#45;&gt;frontend -->
<g id="edge2" class="edge">
<title>proxy&#45;&gt;frontend</title>
<path fill="none" stroke="#9aa5b1" d="M292.52,-84.95C333.17,-105.98 394.79,-137.85 435.96,-159.15"/>
<polygon fill="#9aa5b1" stroke="#9aa5b1" points="434.71,-161.26 442.05,-162.3 436.96,-156.91 434.71,-161.26"/>
<text xml:space="preserve" text-anchor="middle" x="372.69" y="-148.53" font-family="Arial" font-size="9.00" fill="#616e7c">/ → app</text>
</g>
<!-- sidebar_css -->
<g id="node4" class="node">
<title>sidebar_css</title>
<path fill="#ffffff" stroke="#9aa5b1" d="M502.69,-115.44C502.69,-115.44 457.44,-115.44 457.44,-115.44 451.44,-115.44 445.44,-109.44 445.44,-103.44 445.44,-103.44 445.44,-91.44 445.44,-91.44 445.44,-85.44 451.44,-79.44 457.44,-79.44 457.44,-79.44 502.69,-79.44 502.69,-79.44 508.69,-79.44 514.69,-85.44 514.69,-91.44 514.69,-91.44 514.69,-103.44 514.69,-103.44 514.69,-109.44 508.69,-115.44 502.69,-115.44"/>
<text xml:space="preserve" text-anchor="middle" x="480.07" y="-93.74" font-family="Arial" font-size="11.00" fill="#1f2933">sidebar.css</text>
</g>
<!-- proxy&#45;&gt;sidebar_css -->
<g id="edge3" class="edge accent">
<title>proxy&#45;&gt;sidebar_css</title>
<path fill="none" stroke="#3a7dff" stroke-dasharray="5,2" d="M292.52,-69.64C333.52,-75.75 395.85,-85.04 437.01,-91.18"/>
<polygon fill="#3a7dff" stroke="#3a7dff" points="436.42,-93.56 443.71,-92.17 437.15,-88.72 436.42,-93.56"/>
<text xml:space="preserve" text-anchor="middle" x="372.69" y="-89.35" font-family="Arial" font-size="9.00" fill="#3a7dff">injects into &lt;/head&gt;</text>
</g>
<!-- sidebar_js -->
<g id="node5" class="node">
<title>sidebar_js</title>
<path fill="#ffffff" stroke="#9aa5b1" d="M498.57,-61.44C498.57,-61.44 461.57,-61.44 461.57,-61.44 455.57,-61.44 449.57,-55.44 449.57,-49.44 449.57,-49.44 449.57,-37.44 449.57,-37.44 449.57,-31.44 455.57,-25.44 461.57,-25.44 461.57,-25.44 498.57,-25.44 498.57,-25.44 504.57,-25.44 510.57,-31.44 510.57,-37.44 510.57,-37.44 510.57,-49.44 510.57,-49.44 510.57,-55.44 504.57,-61.44 498.57,-61.44"/>
<text xml:space="preserve" text-anchor="middle" x="480.07" y="-39.74" font-family="Arial" font-size="11.00" fill="#1f2933">sidebar.js</text>
</g>
<!-- proxy&#45;&gt;sidebar_js -->
<g id="edge4" class="edge accent">
<title>proxy&#45;&gt;sidebar_js</title>
<path fill="none" stroke="#3a7dff" stroke-dasharray="5,2" d="M292.52,-59.8C334.87,-56.08 399.99,-50.38 441.02,-46.78"/>
<polygon fill="#3a7dff" stroke="#3a7dff" points="441.13,-49.23 447.89,-46.18 440.7,-44.35 441.13,-49.23"/>
</g>
<!-- hub -->
<g id="node6" class="node">
<title>hub</title>
<path fill="#ffffff" stroke="#9aa5b1" d="M706.19,-61.44C706.19,-61.44 627.19,-61.44 627.19,-61.44 621.19,-61.44 615.19,-55.44 615.19,-49.44 615.19,-49.44 615.19,-37.44 615.19,-37.44 615.19,-31.44 621.19,-25.44 627.19,-25.44 627.19,-25.44 706.19,-25.44 706.19,-25.44 712.19,-25.44 718.19,-31.44 718.19,-37.44 718.19,-37.44 718.19,-49.44 718.19,-49.44 718.19,-55.44 712.19,-61.44 706.19,-61.44"/>
<text xml:space="preserve" text-anchor="middle" x="666.69" y="-46.49" font-family="Arial" font-size="11.00" fill="#1f2933">Hub API</text>
<text xml:space="preserve" text-anchor="middle" x="666.69" y="-32.99" font-family="Arial" font-size="11.00" fill="#1f2933">/api/sidebar/config</text>
</g>
<!-- proxy&#45;&gt;hub -->
<g id="edge5" class="edge">
<title>proxy&#45;&gt;hub</title>
<path fill="none" stroke="#9aa5b1" d="M292.27,-45.37C326.42,-31.48 376.57,-13.43 422.44,-5.94 473,2.31 486.99,1.33 537.69,-5.94 560.76,-9.25 585.47,-15.76 606.97,-22.43"/>
<polygon fill="#9aa5b1" stroke="#9aa5b1" points="606.19,-24.75 613.6,-24.53 607.67,-20.08 606.19,-24.75"/>
<text xml:space="preserve" text-anchor="middle" x="480.07" y="-7.89" font-family="Arial" font-size="9.00" fill="#616e7c">/spr/ → soleprint</text>
</g>
<!-- backend -->
<g id="node3" class="node">
<title>backend</title>
<path fill="#ffffff" stroke="#9aa5b1" d="M507.19,-253.44C507.19,-253.44 452.94,-253.44 452.94,-253.44 446.94,-253.44 440.94,-247.44 440.94,-241.44 440.94,-241.44 440.94,-229.44 440.94,-229.44 440.94,-223.44 446.94,-217.44 452.94,-217.44 452.94,-217.44 507.19,-217.44 507.19,-217.44 513.19,-217.44 519.19,-223.44 519.19,-229.44 519.19,-229.44 519.19,-241.44 519.19,-241.44 519.19,-247.44 513.19,-253.44 507.19,-253.44"/>
<text xml:space="preserve" text-anchor="middle" x="480.07" y="-231.74" font-family="Arial" font-size="11.00" fill="#1f2933">Backend API</text>
</g>
<!-- sidebar_js&#45;&gt;hub -->
<g id="edge6" class="edge accent">
<title>sidebar_js&#45;&gt;hub</title>
<path fill="none" stroke="#3a7dff" d="M510.73,-43.44C536.46,-43.44 574.49,-43.44 606.44,-43.44"/>
<polygon fill="#3a7dff" stroke="#3a7dff" points="606.33,-45.89 613.33,-43.44 606.33,-40.99 606.33,-45.89"/>
<text xml:space="preserve" text-anchor="middle" x="572.44" y="-45.39" font-family="Arial" font-size="9.00" fill="#3a7dff">loads config</text>
</g>
<!-- browser -->
<g id="node7" class="node">
<title>browser</title>
<ellipse fill="#ffffff" stroke="#9aa5b1" cx="35.22" cy="-63.44" rx="35.22" ry="18"/>
<text xml:space="preserve" text-anchor="middle" x="35.22" y="-59.74" font-family="Arial" font-size="11.00" fill="#1f2933">Browser</text>
</g>
<!-- browser&#45;&gt;proxy -->
<g id="edge1" class="edge">
<title>browser&#45;&gt;proxy</title>
<path fill="none" stroke="#9aa5b1" d="M70.87,-63.44C105.71,-63.44 159.77,-63.44 199.19,-63.44"/>
<polygon fill="#9aa5b1" stroke="#9aa5b1" points="198.85,-65.89 205.85,-63.44 198.85,-60.99 198.85,-65.89"/>
<text xml:space="preserve" text-anchor="middle" x="128.19" y="-65.39" font-family="Arial" font-size="9.00" fill="#616e7c">myroom.spr.local.ar</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -10,7 +10,7 @@
<title>wrapping</title>
<polygon fill="#0a0a0a" stroke="none" points="-4,4 -4,-325.41 819.55,-325.41 819.55,4 -4,4"/>
<text xml:space="preserve" text-anchor="middle" x="407.78" y="-304.11" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d4a574">Sidebar Injection — How Wrapping Works</text>
<g id="clust1" class="cluster">
<g id="clust1" class="cluster accent">
<title>cluster_nginx</title>
<polygon fill="#0a0a0a" stroke="#d4a574" stroke-dasharray="5,2" points="193.3,-25.16 193.3,-128.16 360.05,-128.16 360.05,-25.16 193.3,-25.16"/>
<text xml:space="preserve" text-anchor="middle" x="276.68" y="-110.86" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d4a574">Nginx (reverse proxy)</text>
@@ -20,19 +20,13 @@
<polygon fill="#0a0a0a" stroke="#333333" stroke-dasharray="5,2" points="473.05,-157.16 473.05,-288.16 599.55,-288.16 599.55,-157.16 473.05,-157.16"/>
<text xml:space="preserve" text-anchor="middle" x="536.3" y="-270.86" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#666666">Managed App</text>
</g>
<g id="clust3" class="cluster">
<g id="clust3" class="cluster accent">
<title>cluster_spr</title>
<polygon fill="#0a0a0a" stroke="#d4a574" stroke-dasharray="5,2" points="489.55,-18.16 489.55,-149.16 807.55,-149.16 807.55,-18.16 489.55,-18.16"/>
<text xml:space="preserve" text-anchor="middle" x="648.55" y="-131.86" font-family="Helvetica,sans-Serif" font-size="14.00" fill="#d4a574">Soleprint</text>
</g>
<!-- browser -->
<g id="node1" class="node">
<title>browser</title>
<ellipse fill="#1a1a1a" stroke="#333333" cx="38.03" cy="-64.16" rx="38.03" ry="18"/>
<text xml:space="preserve" text-anchor="middle" x="38.03" y="-60.46" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">Browser</text>
</g>
<!-- proxy -->
<g id="node2" class="node">
<g id="node1" class="node">
<title>proxy</title>
<polygon fill="#1a1a1a" stroke="#333333" points="324.3,-95.16 228.05,-95.16 228.05,-91.16 224.05,-91.16 224.05,-87.16 228.05,-87.16 228.05,-41.16 224.05,-41.16 224.05,-37.16 228.05,-37.16 228.05,-33.16 324.3,-33.16 324.3,-95.16"/>
<polyline fill="none" stroke="#333333" points="228.05,-91.16 232.05,-91.16 232.05,-87.16 228.05,-87.16"/>
@@ -42,15 +36,8 @@
<text xml:space="preserve" text-anchor="middle" x="276.18" y="-53.71" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">sub_filter</text>
<text xml:space="preserve" text-anchor="middle" x="276.18" y="-40.21" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">injects sidebar</text>
</g>
<!-- browser&#45;&gt;proxy -->
<g id="edge1" class="edge">
<title>browser&#45;&gt;proxy</title>
<path fill="none" stroke="#666666" d="M76.45,-64.16C114.05,-64.16 172.57,-64.16 216.38,-64.16"/>
<polygon fill="#666666" stroke="#666666" points="216.24,-67.66 226.24,-64.16 216.24,-60.66 216.24,-67.66"/>
<text xml:space="preserve" text-anchor="middle" x="138.68" y="-66.86" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#a3a3a3">myroom.spr.local.ar</text>
</g>
<!-- frontend -->
<g id="node3" class="node">
<g id="node2" class="node">
<title>frontend</title>
<polygon fill="#1a1a1a" stroke="#333333" points="591.55,-201.16 481.05,-201.16 481.05,-165.16 591.55,-165.16 591.55,-201.16"/>
<text xml:space="preserve" text-anchor="middle" x="536.3" y="-186.21" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">Frontend</text>
@@ -64,56 +51,69 @@
<text xml:space="preserve" text-anchor="middle" x="416.55" y="-150.7" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#a3a3a3">/ → app</text>
</g>
<!-- sidebar_css -->
<g id="node5" class="node">
<g id="node4" class="node">
<title>sidebar_css</title>
<polygon fill="#1a1a1a" stroke="#333333" points="575.05,-116.16 497.55,-116.16 497.55,-80.16 575.05,-80.16 575.05,-116.16"/>
<text xml:space="preserve" text-anchor="middle" x="536.3" y="-94.46" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">sidebar.css</text>
</g>
<!-- proxy&#45;&gt;sidebar_css -->
<g id="edge4" class="edge">
<g id="edge3" class="edge accent">
<title>proxy&#45;&gt;sidebar_css</title>
<path fill="none" stroke="#d4a574" stroke-dasharray="5,2" d="M324.52,-70.4C370.27,-76.43 439.3,-85.52 485.78,-91.64"/>
<polygon fill="#d4a574" stroke="#d4a574" points="485.21,-95.09 495.58,-92.93 486.12,-88.15 485.21,-95.09"/>
<text xml:space="preserve" text-anchor="middle" x="416.55" y="-90.82" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#a3a3a3">injects into &lt;/head&gt;</text>
<text xml:space="preserve" text-anchor="middle" x="416.55" y="-90.82" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d4a574">injects into &lt;/head&gt;</text>
</g>
<!-- sidebar_js -->
<g id="node6" class="node">
<g id="node5" class="node">
<title>sidebar_js</title>
<polygon fill="#1a1a1a" stroke="#333333" points="570.55,-62.16 502.05,-62.16 502.05,-26.16 570.55,-26.16 570.55,-62.16"/>
<text xml:space="preserve" text-anchor="middle" x="536.3" y="-40.46" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">sidebar.js</text>
</g>
<!-- proxy&#45;&gt;sidebar_js -->
<g id="edge5" class="edge">
<g id="edge4" class="edge accent">
<title>proxy&#45;&gt;sidebar_js</title>
<path fill="none" stroke="#d4a574" stroke-dasharray="5,2" d="M324.52,-60.49C371.88,-56.82 444.19,-51.22 490.6,-47.63"/>
<polygon fill="#d4a574" stroke="#d4a574" points="490.6,-51.14 500.3,-46.87 490.06,-44.16 490.6,-51.14"/>
</g>
<!-- hub -->
<g id="node7" class="node">
<g id="node6" class="node">
<title>hub</title>
<polygon fill="#1a1a1a" stroke="#333333" points="799.55,-62.16 682.3,-62.16 682.3,-26.16 799.55,-26.16 799.55,-62.16"/>
<text xml:space="preserve" text-anchor="middle" x="740.93" y="-47.21" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">Hub API</text>
<text xml:space="preserve" text-anchor="middle" x="740.93" y="-33.71" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">/api/sidebar/config</text>
</g>
<!-- proxy&#45;&gt;hub -->
<g id="edge3" class="edge">
<g id="edge5" class="edge">
<title>proxy&#45;&gt;hub</title>
<path fill="none" stroke="#666666" d="M324.6,-45.85C363.74,-31.76 421.09,-13.48 473.05,-5.91 528.69,2.18 543.83,1.54 599.55,-5.91 624.52,-9.25 651.33,-15.79 674.76,-22.54"/>
<polygon fill="#666666" stroke="#666666" points="673.59,-25.84 684.17,-25.31 675.57,-19.12 673.59,-25.84"/>
<text xml:space="preserve" text-anchor="middle" x="536.3" y="-8.61" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#a3a3a3">/spr/ → soleprint</text>
</g>
<!-- backend -->
<g id="node4" class="node">
<g id="node3" class="node">
<title>backend</title>
<polygon fill="#1a1a1a" stroke="#333333" points="578.05,-255.16 494.55,-255.16 494.55,-219.16 578.05,-219.16 578.05,-255.16"/>
<text xml:space="preserve" text-anchor="middle" x="536.3" y="-233.46" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">Backend API</text>
</g>
<!-- sidebar_js&#45;&gt;hub -->
<g id="edge6" class="edge">
<g id="edge6" class="edge accent">
<title>sidebar_js&#45;&gt;hub</title>
<path fill="none" stroke="#d4a574" d="M570.76,-44.16C597.83,-44.16 636.85,-44.16 670.6,-44.16"/>
<polygon fill="#d4a574" stroke="#d4a574" points="670.3,-47.66 680.3,-44.16 670.3,-40.66 670.3,-47.66"/>
<text xml:space="preserve" text-anchor="middle" x="636.93" y="-46.86" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#a3a3a3">loads config</text>
<text xml:space="preserve" text-anchor="middle" x="636.93" y="-46.86" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#d4a574">loads config</text>
</g>
<!-- browser -->
<g id="node7" class="node">
<title>browser</title>
<ellipse fill="#1a1a1a" stroke="#333333" cx="38.03" cy="-64.16" rx="38.03" ry="18"/>
<text xml:space="preserve" text-anchor="middle" x="38.03" y="-60.46" font-family="Helvetica,sans-Serif" font-size="11.00" fill="#e5e5e5">Browser</text>
</g>
<!-- browser&#45;&gt;proxy -->
<g id="edge1" class="edge">
<title>browser&#45;&gt;proxy</title>
<path fill="none" stroke="#666666" d="M76.45,-64.16C114.05,-64.16 172.57,-64.16 216.38,-64.16"/>
<polygon fill="#666666" stroke="#666666" points="216.24,-67.66 226.24,-64.16 216.24,-60.66 216.24,-67.66"/>
<text xml:space="preserve" text-anchor="middle" x="138.68" y="-66.86" font-family="Helvetica,sans-Serif" font-size="9.00" fill="#a3a3a3">myroom.spr.local.ar</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@@ -1,6 +1,6 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Pawprint Models",
"title": "Soleprint Models",
"description": "Platform-agnostic model definitions. Portable to TypeScript, Pydantic, Django, Prisma.",
"definitions": {
"Status": {

View File

@@ -374,6 +374,21 @@
margin-bottom: 0.5rem;
}
</style>
<!-- theme:baked-defaults — generated by common/theme/bake.py; do not edit -->
<style>
:root {
--bg: #0d0d0f;
--border: #2e2e38;
--border-strong: #3d3d4a;
--dim: #555568;
--muted: #8888a0;
--surface: #16161a;
--system-accent: #d4a574;
--system-accent-text: #e0b98d;
--text: #e8e8f0;
}
</style>
<!-- /theme:baked-defaults -->
<link rel="stylesheet" href="/theme.css">
<style>
/* Artery keeps its own colour under every theme. */
@@ -398,8 +413,8 @@
<circle cx="24" cy="20" r="2" fill="currentColor" />
</svg>
<h1>Artery</h1>
{% if pawprint_url %}<a
href="{{ pawprint_url }}"
{% if soleprint_url %}<a
href="{{ soleprint_url }}"
style="
position: absolute;
right: 0;
@@ -918,7 +933,7 @@
</section>
<footer>
{% if pawprint_url %}<a href="{{ pawprint_url }}">← Soleprint</a>{%
{% if soleprint_url %}<a href="{{ soleprint_url }}">← Soleprint</a>{%
else %}<span class="disabled">← Soleprint</span>{% endif %}
</footer>

View File

@@ -0,0 +1,43 @@
{
"name": "bundle",
"title": "Soleprint Bundle",
"description": "What a rig installation has at its disposal. Exports to a single file that opens with no server.",
"theme": "lucid",
"graph": "system_overview",
"_comment": "A plexus is exported, not served. build.py inlines the theme, the data and the diagram into one index.html so it survives a locked-down Windows box, a zip attachment and a double-click. Nothing here is fetched at runtime.",
"tools": [
{"name": "modelgen", "summary": "Generate models from 6 sources to 9 targets", "standalone": true},
{"name": "datagen", "summary": "Serve rig-owned generators; seed from real rows", "standalone": true},
{"name": "graphgen", "summary": "Generate navigable model graphs", "standalone": true},
{"name": "shuntgen", "summary": "OpenAPI spec or CSV/ODS folder to a running fake service", "standalone": true},
{"name": "tester", "summary": "HTTP contract test runner — one suite, any environment", "standalone": true},
{"name": "databrowse", "summary": "SQL data browser", "standalone": true},
{"name": "sbwrapper", "summary": "Sandbox wrapper", "standalone": true}
],
"cabinets": [
{"name": "postgres", "summary": "Relational database", "rig_addon": "postgres"},
{"name": "redis", "summary": "Cache and broker", "rig_addon": "redis"},
{"name": "airflow", "summary": "Scheduled pipelines", "rig_addon": "airflow", "needs": ["postgres", "redis"]}
],
"veins": [
{"name": "google", "summary": "Sheets and Drive, over OAuth2"},
{"name": "jira", "summary": "Issues and boards"},
{"name": "slack", "summary": "Messages and channels"},
{"name": "ia", "summary": "Model connector"}
],
"themes": [
{"name": "lucid", "summary": "Light, print-ready, shaped after lucid.app", "active": true},
{"name": "soleprint", "summary": "Dark, rounded, amber — the default"},
{"name": "mcrn", "summary": "Dark, square, monospace"}
],
"next": [
"Every tool above is standalone: it runs without the rest of soleprint.",
"Cabinets install as compose services on a laptop, or as rig addons of the same name in a cluster.",
"The diagram below is generated from docs/graphs/*.dot and themed by the same palette as this page."
]
}

View File

@@ -138,6 +138,16 @@
opacity: 0.5;
}
</style>
<!-- theme:baked-defaults — generated by common/theme/bake.py; do not edit -->
<style>
:root {
--bg: #0d0d0f;
--muted: #8888a0;
--system-accent-text: #e0b98d;
--text: #e8e8f0;
}
</style>
<!-- /theme:baked-defaults -->
<link rel="stylesheet" href="/theme.css">
<style>
/* Atlas keeps its own colour under every theme. */

View File

@@ -1,5 +1,5 @@
"""
Album - Documentation system.
Atlas - Documentation system.
"""
import os
@@ -11,7 +11,7 @@ from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
app = FastAPI(title="Album", version="0.1.0")
app = FastAPI(title="Atlas", version="0.1.0")
BASE_DIR = Path(__file__).parent.resolve()
BOOK_DIR = BASE_DIR / "book"
@@ -25,24 +25,24 @@ templates = Jinja2Templates(directory=str(BASE_DIR))
# Serve static files
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
# Pawprint URL for data fetching
PAWPRINT_URL = os.getenv("PAWPRINT_URL", "http://localhost:12000")
# The soleprint hub this atlas reads its data from.
SOLEPRINT_URL = os.getenv("SOLEPRINT_URL", "http://localhost:12000")
def get_data():
"""Fetch data from pawprint hub."""
"""Fetch data from the soleprint hub."""
try:
resp = httpx.get(f"{PAWPRINT_URL}/api/data/album", timeout=5.0)
resp = httpx.get(f"{SOLEPRINT_URL}/api/data/atlas", timeout=5.0)
if resp.status_code == 200:
return resp.json()
except Exception as e:
print(f"Failed to fetch data from pawprint: {e}")
return {"templates": [], "larders": [], "books": []}
print(f"Failed to fetch data from soleprint: {e}")
return {"templates": [], "depots": [], "books": []}
@app.get("/health")
def health():
return {"status": "ok", "service": "album"}
return {"status": "ok", "service": "atlas"}
@app.get("/")
@@ -52,7 +52,7 @@ def index(request: Request):
"index.html",
{
"request": request,
"pawprint_url": os.getenv("PAWPRINT_EXTERNAL_URL", PAWPRINT_URL),
"soleprint_url": os.getenv("SOLEPRINT_EXTERNAL_URL", SOLEPRINT_URL),
**data,
},
)
@@ -60,7 +60,7 @@ def index(request: Request):
@app.get("/api/data")
def api_data():
"""API endpoint for frontend data (proxied from pawprint)."""
"""API endpoint for frontend data (proxied from soleprint)."""
return get_data()
@@ -130,7 +130,7 @@ def feature_form_samples_template():
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Feature Form Template · Album</title>
<title>Feature Form Template · Atlas</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
@@ -229,7 +229,7 @@ def feature_form_samples_template():
<div class="container">
<header>
<div class="breadcrumb">
<a href="/">Album</a> / <a href="/book/feature-form-samples/">Feature Form Samples</a> / Template
<a href="/">Atlas</a> / <a href="/book/feature-form-samples/">Feature Form Samples</a> / Template
</div>
<h1>Feature Form Template</h1>
<div class="meta">
@@ -244,7 +244,7 @@ def feature_form_samples_template():
<div class="form-body">
<div class="field">
<label class="field-label">Tipo de Usuario</label>
<div class="field-value">[Dueno de mascota / Veterinario / Admin]</div>
<div class="field-value">[Tipo A / Tipo B / Admin]</div>
</div>
<div class="field">
<label class="field-label">Punto de Entrada</label>
@@ -296,25 +296,25 @@ def feature_form_samples_template():
return HTMLResponse(html)
@app.get("/book/feature-form-samples/larder/", response_class=HTMLResponse)
@app.get("/book/feature-form-samples/larder", response_class=HTMLResponse)
def feature_form_samples_larder():
"""Browse the larder (actual data)"""
@app.get("/book/feature-form-samples/depot/", response_class=HTMLResponse)
@app.get("/book/feature-form-samples/depot", response_class=HTMLResponse)
def feature_form_samples_depot():
"""Browse the depot (actual data)"""
html_file = BOOK_DIR / "feature-form-samples" / "index.html"
if html_file.exists():
return HTMLResponse(html_file.read_text())
return HTMLResponse("<h1>Larder index not found</h1>", status_code=404)
return HTMLResponse("<h1>Depot index not found</h1>", status_code=404)
@app.get(
"/book/feature-form-samples/larder/{user_type}/{filename}",
"/book/feature-form-samples/depot/{user_type}/{filename}",
response_class=HTMLResponse,
)
def feature_form_samples_detail(request: Request, user_type: str, filename: str):
"""View a specific feature form"""
# Look in the larder subfolder (feature-form)
larder_dir = BOOK_DIR / "feature-form-samples" / "feature-form"
file_path = larder_dir / user_type / filename
# Look in the depot subfolder (feature-form)
depot_dir = BOOK_DIR / "feature-form-samples" / "feature-form"
file_path = depot_dir / user_type / filename
if not file_path.exists():
return HTMLResponse("<h1>Not found</h1>", status_code=404)

View File

@@ -0,0 +1,177 @@
#!/usr/bin/env python3
"""
Bake the default palette into the pages that use it.
python3 common/theme/bake.py # rewrite the baked blocks
python3 common/theme/bake.py --check # fail if any page is stale
A page that says `background: var(--bg)` and never gets `--bg` does not fall
back to something plainer — the declaration is invalid at computed-value time,
so the background goes transparent and the text goes initial-black on a design
that assumed dark. Unstyled, not merely unbranded.
That matters because `/theme.css` is an absolute path and soleprint is not
always at the root. In the sample room's nginx, soleprint sits under `/spr/`
while `location /` proxies to the frontend, so `/theme.css` reaches the wrong
service. Same story for a page opened over file://.
So every page carries a baked default: a `:root` block with literal values,
emitted BEFORE the `<link>`. Both are `:root`, so document order decides — the
served stylesheet wins whenever it loads, and the baked block is what is left
when it does not. Nothing is given up in either direction.
The block is generated rather than hand-written, which is the point: the values
come from tokens.css and the default theme, so there is still one source. Only
the variables a page actually uses are emitted, so the blocks stay small.
"""
import re
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
SPR_ROOT = HERE.parent.parent # soleprint/
TOKENS = HERE / "tokens.css"
DEFAULT_THEME = HERE / "themes" / "soleprint.css"
BEGIN = "<!-- theme:baked-defaults — generated by common/theme/bake.py; do not edit -->"
END = "<!-- /theme:baked-defaults -->"
# Pages that link /theme.css and therefore need a default to fall back on.
PAGES = [
"index.html",
"artery/index.html",
"atlas/index.html",
"station/index.html",
"station/tools/datagen/templates/index.html",
"station/tools/graphgen/templates/index.html",
"station/tools/shuntgen/templates/index.html",
]
def declarations(css: str, selector: str) -> dict[str, str]:
"""Pull `--name: value;` pairs out of one rule."""
match = re.search(re.escape(selector) + r"\s*\{(.*?)\n\}", css, re.S)
if not match:
return {}
out = {}
for name, value in re.findall(r"(--[\w-]+)\s*:\s*([^;]+);", match.group(1)):
out[name] = value.strip()
return out
def resolve(name: str, table: dict[str, str], seen: frozenset = frozenset()) -> str | None:
"""Flatten a value to literals, following var() chains and honouring fallbacks."""
if name in seen or name not in table:
return None
value = table[name]
def swap(match: re.Match) -> str:
inner = match.group(1)
# var(--x, fallback) — the fallback may itself contain commas.
if "," in inner:
ref, fallback = inner.split(",", 1)
ref, fallback = ref.strip(), fallback.strip()
else:
ref, fallback = inner.strip(), None
resolved = resolve(ref, table, seen | {name})
if resolved is not None:
return resolved
return fallback if fallback is not None else ""
for _ in range(10): # chains are shallow; the bound just stops a cycle
new = re.sub(r"var\(\s*([^()]*(?:\([^()]*\)[^()]*)*)\)", swap, value)
if new == value:
break
value = new
return value.strip() or None
def palette() -> dict[str, str]:
"""Every token, flattened to literals, with the default theme applied."""
tokens = declarations(TOKENS.read_text(), ":root")
tokens.update(declarations(DEFAULT_THEME.read_text(), '[data-theme="soleprint"]'))
return {name: resolve(name, tokens) for name in tokens}
def used(html: str) -> set[str]:
"""Variables a page references, ignoring the baked block itself."""
body = re.sub(re.escape(BEGIN) + r".*?" + re.escape(END), "", html, flags=re.S)
return set(re.findall(r"var\(\s*(--[\w-]+)", body))
def block(names: set[str], values: dict[str, str], indent: str) -> str:
"""The baked <style>, wrapped in markers so it can be replaced next time."""
lines = [f"{indent}{BEGIN}", f"{indent}<style>", f"{indent} :root {{"]
for name in sorted(names):
value = values.get(name)
if value:
lines.append(f"{indent} {name}: {value};")
lines += [f"{indent} }}", f"{indent}</style>", f"{indent}{END}"]
return "\n".join(lines)
def bake(path: Path, values: dict[str, str]) -> tuple[bool, str]:
"""Return (changed, note) for one page."""
html = path.read_text()
link = re.search(r'([ \t]*)<link rel="stylesheet" href="/theme.css">', html)
if not link:
return False, "no /theme.css link — skipped"
indent = link.group(1)
names = used(html)
if not names:
return False, "uses no theme variables — skipped"
fresh = block(names, values, indent)
existing = re.search(re.escape(BEGIN) + r".*?" + re.escape(END), html, re.S)
if existing:
updated = html[: existing.start()] + fresh.lstrip() + html[existing.end() :]
else:
# Before the link, never after: document order is what makes the served
# stylesheet win over the baked one.
updated = html[: link.start()] + fresh + "\n" + html[link.start() :]
if updated == html:
return False, f"up to date ({len(names)} variables)"
path.write_text(updated)
return True, f"baked {len(names)} variables"
def main() -> int:
check = "--check" in sys.argv
values = palette()
missing = [n for n, v in values.items() if not v]
if missing:
print(f"warning: unresolved tokens: {', '.join(sorted(missing))}", file=sys.stderr)
stale = []
for rel in PAGES:
path = SPR_ROOT / rel
if not path.exists():
print(f" {rel}: not found")
continue
if check:
before = path.read_text()
changed, note = bake(path, values)
if changed:
path.write_text(before)
stale.append(rel)
print(f" {rel}: STALE")
else:
print(f" {rel}: {note}")
else:
_, note = bake(path, values)
print(f" {rel}: {note}")
if check and stale:
print(f"\n{len(stale)} page(s) stale — run: python3 common/theme/bake.py", file=sys.stderr)
return 1
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -19,7 +19,11 @@
(function () {
"use strict";
var THEMES = ["soleprint", "mcrn"];
// Order is the toggle order. soleprint stays first because it is the
// default; lucid is last because it is the one you switch to on purpose,
// to show someone something.
var THEMES = ["soleprint", "mcrn", "lucid"];
var LABELS = { soleprint: "SPR", mcrn: "MCRN", lucid: "LUCID" };
var KEY = "spr-theme";
var root = document.documentElement;
@@ -82,7 +86,7 @@
var button = document.createElement("button");
button.type = "button";
button.dataset.theme = theme;
button.textContent = theme === "mcrn" ? "MCRN" : "SPR";
button.textContent = LABELS[theme] || theme.toUpperCase();
button.title = "Switch to the " + theme + " theme";
button.addEventListener("click", function () {
apply(theme, true);

View File

@@ -0,0 +1,150 @@
/* Lucid — the regulated-document look, shaped after lucid.app exports.
*
* Why it exists: the deliverable gets shown on Windows, printed, and pasted next
* to real Lucidchart diagrams. Dark developer chrome cannot go in that room. The
* target is that a generated page and a genuine Lucid export sit side by side
* without announcing which is which.
*
* This is the first LIGHT theme here, and that is the part that needed care —
* tokens.css and both sibling themes were written assuming near-black. Two
* things do not survive the inversion and are overridden below rather than
* inherited:
*
* - the glow. A coloured halo means "lit" against black; against white it just
* looks like a rendering fault. Replaced with a hairline drop shadow.
* - --dim as body-adjacent text. At #555568 on white it fails contrast, so the
* ramp is rebuilt from the light end rather than reused.
*
* Fonts are stacks, never a webfont: this has to render with no egress. Arial is
* last because it is the one face guaranteed on Windows and aliased on Linux —
* the same reason the graphviz themes name it (see docs/graphs/themes/).
*/
[data-theme="lucid"] {
color-scheme: light;
--bg: #ffffff;
--bg-2: #f5f7fa;
--surface: #f5f7fa;
--surface-raised: #e4e7eb;
--border: #cbd2d9;
--border-strong: #9aa5b1;
/* Measured against both #ffffff and the #f5f7fa panel, because --dim is used
* for 11px notes and --status-warn for 10px labels — sizes where AA wants
* 4.5:1, not the 3:1 that large text gets away with. The obvious lighter
* greys (#7b8794, #73808d) come in at 3.43.8 on the panel and were dropped
* for that reason. */
--text: #1f2933; /* 14.76 on white — near-black; pure #000 reads harsh in print */
--muted: #616e7c; /* 5.21 / 4.86 */
--dim: #66717d; /* 4.97 / 4.63 */
--accent: #3a7dff;
--accent-dim: #2f6ae0;
--accent-text: #1c5bd9; /* darkened: the fill blue is too light for small text */
--glow: rgba(58, 125, 255, 0.18);
--status-ok: #0b875b; /* 4.53 / 4.23 */
--status-info: #1c5bd9; /* 5.92 / 5.52 */
--status-warn: #a35f00; /* 5.01 / 4.67 — #b06a00 was 3.99 on the panel */
--status-error: #cf2e2e; /* 5.14 / 4.79 */
--status-idle: #9aa5b1; /* dots and rules only, never text */
--radius-sm: 4px;
--radius: 6px;
--radius-lg: 8px;
--radius-xl: 8px;
--font-ui: "Segoe UI", Inter, system-ui, -apple-system, Arial, sans-serif;
--font-mono: "Cascadia Mono", Consolas, "JetBrains Mono", monospace;
--font-heading: "Segoe UI", Inter, system-ui, Arial, sans-serif;
--heading-transform: none;
--heading-spacing: 0;
--heading-weight: 600;
--label-spacing: 0.02em;
--speed-fast: 0.12s;
--speed: 0.18s;
/* Elevation, not luminosity. */
--hover-shadow: 0 1px 3px rgba(16, 24, 40, 0.1), 0 1px 2px rgba(16, 24, 40, 0.06);
--hover-lift: none;
--focus-shadow: 0 0 0 2px rgba(58, 125, 255, 0.35);
}
/* Panels are white cards on a pale canvas — the inverse of the dark themes,
* where the panel is lighter than the page. */
[data-theme="lucid"] .panel,
[data-theme="lucid"] .card,
[data-theme="lucid"] .model-card {
background: #ffffff;
}
[data-theme="lucid"] .card:hover,
[data-theme="lucid"] .panel:hover,
[data-theme="lucid"] .system-card:hover,
[data-theme="lucid"] .tool-card:hover,
[data-theme="lucid"] .model-card:hover {
border-color: var(--system-accent, var(--accent));
box-shadow: var(--hover-shadow);
transform: none;
}
/* A solid fill with white knocked out, the way a Lucid toolbar reads. No
* gradient: gradients are the first thing that looks wrong in print. */
[data-theme="lucid"] button[aria-pressed="true"],
[data-theme="lucid"] .active,
[data-theme="lucid"] .selected {
background: var(--accent);
border-color: var(--accent);
color: #ffffff;
}
[data-theme="lucid"] .label,
[data-theme="lucid"] .panel-title {
font-size: var(--font-size-sm);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--muted);
}
/* Pale-fill chips, the shape Lucid uses for tags on a shape. */
[data-theme="lucid"] .badge {
border-radius: var(--radius-sm);
padding: 1px 6px;
font-family: var(--font-mono);
font-size: 10px;
background: #eaf0ff;
border: 1px solid #c3d4ff;
color: var(--accent-text);
}
[data-theme="lucid"] code,
[data-theme="lucid"] pre {
background: #f5f7fa;
border-color: var(--border);
}
/* Diagrams are the point of this theme, and they are rendered as <img>, so the
* page cannot colour them — it can only stop fighting them. A white-canvas SVG
* needs a frame to read as a figure rather than as a hole in the page. */
[data-theme="lucid"] img[src$=".svg"] {
background: #ffffff;
border: 1px solid var(--border);
border-radius: var(--radius);
}
/* Printing is a first-class output here: this theme exists to end up in a
* document. Drop the chrome that has no meaning on paper. */
@media print {
[data-theme="lucid"] #spr-theme-toggle,
[data-theme="lucid"] #spr-sidebar {
display: none !important;
}
[data-theme="lucid"] .panel,
[data-theme="lucid"] .card {
box-shadow: none;
break-inside: avoid;
}
}

View File

@@ -42,9 +42,9 @@
--radius-lg: 0;
--radius-xl: 0;
--font-ui: "JetBrains Mono", "Fira Code", "SF Mono", monospace;
--font-mono: "JetBrains Mono", "Fira Code", "SF Mono", monospace;
--font-heading: "JetBrains Mono", "Fira Code", "SF Mono", monospace;
--font-ui: "JetBrains Mono", "Cascadia Mono", Consolas, "SF Mono", monospace;
--font-mono: "JetBrains Mono", "Cascadia Mono", Consolas, "SF Mono", monospace;
--font-heading: "JetBrains Mono", "Cascadia Mono", Consolas, "SF Mono", monospace;
--heading-transform: uppercase;
--heading-spacing: 0.1em;
--heading-weight: 400;

View File

@@ -41,9 +41,9 @@
--radius-lg: 8px;
--radius-xl: 12px;
--font-ui: "Inter", system-ui, -apple-system, sans-serif;
--font-mono: "JetBrains Mono", "Fira Code", monospace;
--font-heading: "Inter", system-ui, sans-serif;
--font-ui: Inter, "Segoe UI", system-ui, -apple-system, Arial, sans-serif;
--font-mono: "JetBrains Mono", "Cascadia Mono", Consolas, monospace;
--font-heading: Inter, "Segoe UI", system-ui, Arial, sans-serif;
--heading-transform: none;
--heading-spacing: 0.02em;
--heading-weight: 600;

View File

@@ -20,7 +20,11 @@
* handler beside /sidebar.css.
*/
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;600&display=swap");
/* No webfont import. This has to render on a locked-down Windows box with no
* egress and from a double-clicked file, and a blocked stylesheet there is a
* blank page or a stall, not a fallback. The stacks below resolve to something
* deliberate on every target: Segoe UI and Consolas ship with Windows, Inter and
* JetBrains Mono are picked up where they happen to be installed. */
:root {
/* ── surfaces ─────────────────────────────────────────────────────── */
@@ -63,8 +67,8 @@
--hairline: 1px;
/* ── type ─────────────────────────────────────────────────────────── */
--font-ui: "Inter", system-ui, -apple-system, sans-serif;
--font-mono: "JetBrains Mono", "Fira Code", "SF Mono", monospace;
--font-ui: "Segoe UI", Inter, system-ui, -apple-system, Arial, sans-serif;
--font-mono: "Cascadia Mono", "JetBrains Mono", Consolas, "SF Mono", monospace;
--font-heading: var(--font-ui);
--font-size-sm: 11px;
--font-size-base: 13px;

View File

@@ -249,6 +249,20 @@
<link rel="stylesheet" href="/sidebar.css">
<script src="/sidebar.js"></script>
{% endif %}
<!-- theme:baked-defaults — generated by common/theme/bake.py; do not edit -->
<style>
:root {
--accent: #d4a574;
--accent-dim: #b8956a;
--bg: #0d0d0f;
--border: #2e2e38;
--dim: #555568;
--muted: #8888a0;
--surface: #16161a;
--text: #e8e8f0;
}
</style>
<!-- /theme:baked-defaults -->
<link rel="stylesheet" href="/theme.css">
</head>
<body{% if managed %} class="has-sidebar"{% endif %}>

View File

@@ -601,11 +601,21 @@ def station_route(path: str):
# page can switch themes without a second request and without FOUC.
def available_themes() -> list[str]:
"""Theme names, from the files themselves — adding one is adding a file."""
theme_dir = SPR_ROOT / "common" / "theme" / "themes"
if not theme_dir.exists():
return ["soleprint"]
names = sorted(p.stem for p in theme_dir.glob("*.css"))
# Default first, so a consumer taking names[0] gets the sensible one.
return sorted(names, key=lambda n: (n != "soleprint", n))
def get_default_theme() -> str:
"""The theme a page is served in, before the browser has an opinion."""
framework = load_config().get("framework", {})
theme = framework.get("theme", "soleprint")
return theme if theme in ("soleprint", "mcrn") else "soleprint"
return theme if theme in available_themes() else "soleprint"
@app.get("/theme.css")
@@ -652,7 +662,7 @@ def theme_js():
@app.get("/api/theme")
def theme_config():
"""The server-side default, for pages that render their own <html> tag."""
return {"theme": get_default_theme(), "themes": ["soleprint", "mcrn"]}
return {"theme": get_default_theme(), "themes": available_themes()}
@app.get("/sidebar.css")

View File

@@ -157,6 +157,20 @@
opacity: 0.5;
}
</style>
<!-- theme:baked-defaults — generated by common/theme/bake.py; do not edit -->
<style>
:root {
--bg: #0d0d0f;
--border: #2e2e38;
--border-strong: #3d3d4a;
--muted: #8888a0;
--surface: #16161a;
--system-accent: #d4a574;
--system-accent-text: #e0b98d;
--text: #e8e8f0;
}
</style>
<!-- /theme:baked-defaults -->
<link rel="stylesheet" href="/theme.css">
<style>
/* Station keeps its own colour under every theme. */

View File

@@ -15,26 +15,26 @@ This monitor provides at-a-glance views of the database grouped by test-relevant
## Architecture
Follows pawprint **book/larder pattern**:
- **larder/** contains all data files (schema, views, scenarios)
Follows the book/depot pattern:
- **depot/** contains all data files (schema, views, scenarios)
- **main.py** generates SQL queries from view definitions
- Two modes: **SQL** (direct queries) and **API** (Django backend, placeholder)
### Key Concepts
**Schema** (`larder/schema.json`)
**Schema** (`depot/schema.json`)
- AMAR data model with SQL table mappings
- Regular fields (from database columns)
- Computed fields (SQL expressions)
- Support for multiple graph generators
**Views** (`larder/views.json`)
**Views** (`depot/views.json`)
- Define what to display and how to group it
- Each view targets an entity (User, PetOwner, Veterinarian, etc.)
- Can group results (e.g., by role, by data state, by availability)
- SQL is generated automatically from view configuration
**Scenarios** (`larder/scenarios.json`)
**Scenarios** (`depot/scenarios.json`)
- Test scenarios emerge from actual usage
- Format defined, real scenarios added as needed
- Links scenarios to specific views with filters
@@ -49,21 +49,21 @@ Follows pawprint **book/larder pattern**:
## Running Locally
```bash
cd /home/mariano/wdir/ama/pawprint/ward/monitor/data_browse
cd soleprint/station/monitors/databrowse
python main.py
# Opens on http://localhost:12020
```
Or with uvicorn:
```bash
uvicorn ward.monitor.data_browse.main:app --port 12020 --reload
uvicorn station.monitors.databrowse.main:app --port 12020 --reload
```
## Environment Variables
```bash
# Database connection (defaults to local dev)
export NEST_NAME=local
export ROOM_NAME=local
export DB_HOST=localhost
export DB_PORT=5433
export DB_NAME=amarback
@@ -85,7 +85,7 @@ GET /api/scenarios # Test scenarios (JSON)
## Adding New Views
Edit `larder/views.json`:
Edit `depot/views.json`:
```json
{
@@ -112,7 +112,7 @@ The SQL query is automatically generated from:
## Adding Computed Fields
Edit `larder/schema.json` in the entity definition:
Edit `depot/schema.json` in the entity definition:
```json
"computed": {
@@ -127,7 +127,7 @@ Computed fields can be used in views just like regular fields.
## Adding Test Scenarios
As you identify test patterns, add them to `larder/scenarios.json`:
As you identify test patterns, add them to `depot/scenarios.json`:
```json
{
@@ -153,8 +153,8 @@ As you identify test patterns, add them to `larder/scenarios.json`:
```
data_browse/
├── larder/
│ ├── .larder # Larder marker (book pattern)
├── depot/
│ ├── .depot # Depot marker (book pattern)
│ ├── schema.json # AMAR data model with SQL mappings
│ ├── views.json # View configurations
│ └── scenarios.json # Test scenarios

View File

@@ -245,7 +245,7 @@
{% if views|length == 0 %}
<div class="empty">
No views configured. Add views to larder/views.json
No views configured. Add views to depot/views.json
</div>
{% else %}
<div class="card-grid">
@@ -289,7 +289,7 @@
<div class="empty">
No scenarios defined yet. Scenarios emerge from usage and
conversations.
<br />Add them to larder/scenarios.json as you identify test
<br />Add them to depot/scenarios.json as you identify test
patterns.
</div>
{% else %}

View File

@@ -6,6 +6,21 @@
<title>datagen — Test Data Generator</title>
<!-- Palette, fonts and the theme switcher. The :root block that used to sit
here was one of eight copies that had already drifted apart. -->
<!-- theme:baked-defaults — generated by common/theme/bake.py; do not edit -->
<style>
:root {
--amber: #d4a574;
--amber-dim: #b8956a;
--bg: #0d0d0f;
--border: #2e2e38;
--dim: #555568;
--font-ui: Inter, "Segoe UI", system-ui, -apple-system, Arial, sans-serif;
--muted: #8888a0;
--surface: #16161a;
--text: #e8e8f0;
}
</style>
<!-- /theme:baked-defaults -->
<link rel="stylesheet" href="/theme.css">
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }

View File

@@ -5,6 +5,21 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>graphgen — Schema Explorer</title>
<!-- Palette, fonts and the theme switcher — see common/theme/. -->
<!-- theme:baked-defaults — generated by common/theme/bake.py; do not edit -->
<style>
:root {
--amber: #d4a574;
--amber-dim: #b8956a;
--bg: #0d0d0f;
--border: #2e2e38;
--dim: #555568;
--font-ui: Inter, "Segoe UI", system-ui, -apple-system, Arial, sans-serif;
--muted: #8888a0;
--surface: #16161a;
--text: #e8e8f0;
}
</style>
<!-- /theme:baked-defaults -->
<link rel="stylesheet" href="/theme.css">
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pawprint Wrapper - Demo</title>
<title>Sidebar Wrapper - Demo</title>
<link rel="stylesheet" href="sidebar.css">
<style>
/* Demo page styles */
@@ -19,7 +19,7 @@
transition: margin-right 0.3s ease;
}
#pawprint-sidebar.expanded ~ #demo-content {
#spr-sidebar.expanded ~ #demo-content {
margin-right: var(--sidebar-width);
}
@@ -105,14 +105,14 @@
<div id="demo-content">
<div class="demo-header">
<h1>🐾 Pawprint Wrapper</h1>
<p>Development tools sidebar for any pawprint-managed nest</p>
<h1>Sidebar Wrapper</h1>
<p>Development tools sidebar for any soleprint-managed room</p>
</div>
<div class="demo-section">
<h2>👋 Quick Start</h2>
<p>
This is a standalone demo of the Pawprint Wrapper sidebar.
This is a standalone demo of the sidebar wrapper.
Click the toggle button on the right edge of the screen, or press
<span class="kbd">Ctrl</span> + <span class="kbd">Shift</span> + <span class="kbd">P</span>
to open the sidebar.

View File

@@ -4,6 +4,37 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>shuntgen</title>
<!-- theme:baked-defaults — generated by common/theme/bake.py; do not edit -->
<style>
:root {
--accent: #d4a574;
--accent-dim: #b8956a;
--accent-text: #e0b98d;
--bg: #0d0d0f;
--bg-2: #16161a;
--border: #2e2e38;
--dim: #555568;
--font-mono: "JetBrains Mono", "Cascadia Mono", Consolas, monospace;
--font-ui: Inter, "Segoe UI", system-ui, -apple-system, Arial, sans-serif;
--hairline: 1px;
--label-spacing: 0.04em;
--muted: #8888a0;
--radius: 6px;
--radius-lg: 8px;
--radius-sm: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-6: 24px;
--status-error: #f06565;
--status-info: #4f9cf9;
--status-ok: #3ecf8e;
--status-warn: #f5a623;
--surface: #16161a;
--text: #e8e8f0;
}
</style>
<!-- /theme:baked-defaults -->
<link rel="stylesheet" href="/theme.css">
<style>
* { box-sizing: border-box; }

View File

@@ -1,6 +0,0 @@
# Contract HTTP Tests - Environment Configuration
#
# Get API key: ./get-api-key.sh --docker core_nest_db
CONTRACT_TEST_URL=http://backend:8000
CONTRACT_TEST_API_KEY=118b1fcca089496919f0d82df2c4c89d35126793dfc3ea645366ae09d931f49f

View File

@@ -0,0 +1,19 @@
# Contract test target. Copy to .env and fill in — .env is gitignored.
#
# cp .env.example .env
#
# Environment variables override this file (see config.py), so CI can set these
# without a file at all:
#
# CONTRACT_TEST_URL=https://staging.example.com python -m tester run
#
# A real key was committed here once. Keep credentials in .env or the
# environment; environments.json is tracked and must stay empty of them.
CONTRACT_TEST_URL=http://localhost:8000
# One of: bearer (default) | api-key | none
CONTRACT_TEST_AUTH_TYPE=none
# CONTRACT_TEST_API_KEY=
# CONTRACT_TEST_TOKEN=

View File

@@ -154,11 +154,11 @@ ward/tools/tester/
If running standalone:
```bash
cd /home/mariano/wdir/ama/pawprint/ward/tools/tester
cd soleprint/station/tools/tester
python -m uvicorn main:app --reload --port 12003
```
Or if integrated with ward:
Or if integrated with station:
```bash
# Ward service should pick it up automatically
```

View File

@@ -16,7 +16,9 @@ set -e
# Defaults
DB_HOST="${DB_HOST:-localhost}"
DB_PORT="${DB_PORT:-5432}"
DB_NAME="${DB_NAME:-amarback}"
# No default database name: this ships in an open framework, and a client's
# schema name is not a sensible fallback. Set DB_NAME or pass --name.
DB_NAME="${DB_NAME:-}"
DB_USER="${DB_USER:-postgres}"
DB_PASSWORD="${DB_PASSWORD:-}"
DOCKER_CONTAINER=""
@@ -25,7 +27,7 @@ DOCKER_CONTAINER=""
while [[ $# -gt 0 ]]; do
case $1 in
--docker)
DOCKER_CONTAINER="${2:-core_nest_db}"
DOCKER_CONTAINER="${2:-}"
shift 2 || shift 1
;;
--host)
@@ -52,10 +54,10 @@ while [[ $# -gt 0 ]]; do
echo "Usage: $0 [options]"
echo ""
echo "Options:"
echo " --docker [container] Query via docker exec (default: core_nest_db)"
echo " --docker <container> Query via docker exec (container name required)"
echo " --host HOST Database host"
echo " --port PORT Database port (default: 5432)"
echo " --name NAME Database name (default: amarback)"
echo " --name NAME Database name (required, or set DB_NAME)"
echo " --user USER Database user (default: postgres)"
echo " --password PASS Database password"
echo ""
@@ -69,6 +71,13 @@ while [[ $# -gt 0 ]]; do
esac
done
if [[ -z "$DB_NAME" ]]; then
echo "no database name — pass --name NAME or set DB_NAME" >&2
echo "(there is no default: this ships in an open framework, and a client's" >&2
echo " schema name is not a sensible fallback)" >&2
exit 1
fi
QUERY="SELECT key FROM common_apikey WHERE is_active=true LIMIT 1;"
if [[ -n "$DOCKER_CONTAINER" ]]; then

View File

@@ -6,7 +6,6 @@ Tests basic HTTP connectivity and authentication flow.
"""
from ..base import ContractTestCase
from ..endpoints import Endpoints
class TestHealth(ContractTestCase):