updates 33.2 112

This commit is contained in:
2026-08-11 07:30:27 -03:00
parent 910927993e
commit 2d9bc9289c
17 changed files with 1287 additions and 109 deletions

View File

@@ -0,0 +1,93 @@
# Plexuses
A **plexus** is a full app — the vocabulary has always said so
(*"full app with backend, frontend and DB"*). What was missing is that a plexus
is **exported, not served**. It is compiled to a distributable file the way vite
builds for production, and that compile is the whole point of the format.
```bash
make dist # compile the plexus UIs for standalone
make dist sample # for a named room
```
Output is one `index.html` per plexus under `gen/<room>/plexuses/<name>/`,
carrying its theme, its data and its diagrams inline. No server, no node, no
network. Zip it, mail it, double-click it.
`make build` runs the same step as part of a room build. `make dist` exists for
the loop where the UI is what you are working on — rebuilding a whole room to
see a CSS change is a slow way to iterate.
## The constraint that shapes it
It has to open from a **double-clicked file on a machine with no egress**. That
is the state a regulated Windows box is usually in, and it rules out three
things a normal web app does:
| Ruled out | Because |
| --- | --- |
| `fetch("bundle.json")` | `file://` treats every sibling file as cross-origin |
| `<link href="/theme.css">` | an absolute path assumes a server at the root |
| a webfont `@import` | a blocked stylesheet is a stall, not a fallback |
So the data is a JS object, the theme is inlined at compile time, and the fonts
are stacks. The test that matters is opening the output with the network off and
seeing zero failed requests — everything else is cosmetic.
## Declaring one
A room opts in through `cfg/<room>/data/plexuses.json`, the same shape and the
same place as its sibling `data/*.json` files:
```json
{ "items": [ { "name": "bundle" } ] }
```
A room may override anything the plexus declares — most usefully the theme:
```json
{ "items": [ { "name": "bundle", "theme": "mcrn" } ] }
```
## Writing one
```
soleprint/artery/plexuses/<name>/
plexus.json identity, theme, the data the page renders
app/index.html the template
```
`build.py` fills these placeholders and writes one file:
| Placeholder | Becomes |
| --- | --- |
| `%%THEME_CSS%%` | tokens plus every theme, so the switcher has something to switch to |
| `%%BUNDLE%%` | `plexus.json` as a JS object |
| `%%GRAPH%%` | a rendered SVG from `docs/graphs/`, inlined |
| `%%TITLE%%` `%%DESCRIPTION%%` `%%DEFAULT_THEME%%` `%%BUILT%%` | from the manifest |
Anything else in `app/` is copied alongside, for a plexus that outgrows one file.
## The bundle plexus
The one that ships. It answers "what does a rig installation have at its
disposal" — tools, cabinets, veins, themes — and embeds the system diagram.
Because the SVG is **inlined** rather than `<img>`-linked, the theme switch
recolours the diagram too: graphviz writes `class="node accent"` into the SVG,
and CSS outranks the presentation attributes it bakes in. Switching to `lucid`
turns both the page and the diagram into something printable, which is the
demonstration the format exists for.
## Not the same as rig's bundle
Two artifacts, both called bundle, generated by different repos:
| | soleprint | rig |
| --- | --- | --- |
| Command | `make dist` | `make manifest` in `sample-rig` |
| Artifact | `plexuses/<name>/index.html` | `generated/<slug>.yaml` |
| Needs | nothing | kind + MetalLB |
| Answers | what shipped, on any machine | whether this cluster install is sound |
Complementary. One proves the environment, the other travels.

View File

@@ -19,6 +19,8 @@ make start # run it
| Command | Runs | Does |
| --- | --- | --- |
| `make build [<room>\|all\|models]` | `ctrl/build.sh` | compile a room into `gen/` |
| `make dist [<room>]` | `ctrl/dist.sh` | compile just the plexus UIs to single files |
| `make docs [serve\|graphs]` | `ctrl/docs.sh` | serve the docs, or re-render the diagrams |
| `make start [<room>] [-d]` | `ctrl/start.sh` | run a built room's compose stack |
| `make stop [<room>]` | `ctrl/stop.sh` | stop it |
| `make cluster [up\|down\|status]` | `ctrl/cluster.sh` | the shared kind cluster |
@@ -50,9 +52,12 @@ make component ARGS="publish soleprint-ui /tmp/out --dist"
4. **Compose cabinets.** The dependency containers the room declared in
`data/cabinets.json` are merged into its `docker-compose.yml`. See
[Cabinets](#station-cabinets).
5. **Generate models.** modelgen reads the room's `config.json` and writes
5. **Export plexuses.** Each plexus the room declared in `data/plexuses.json` is
compiled to a single self-contained `index.html` — theme, data and diagrams
inlined, so it opens with no server. See [Plexuses](#artery-plexuses).
6. **Generate models.** modelgen reads the room's `config.json` and writes
`models/pydantic/__init__.py`.
6. **Render k8s** (optional). When the room's config enables it,
7. **Render k8s** (optional). When the room's config enables it,
`soleprint/ctrl/k8s/` writes manifests and lifecycle scripts.
## What comes out
@@ -66,6 +71,7 @@ gen/standalone/
cfg/config.json
data/*.json
models/pydantic/
plexuses/<name>/index.html # one file each, opens with no server
```
A **managed** room — one that wraps an existing application — is three folders
@@ -122,3 +128,17 @@ the far side. `.env` is excluded, so server secrets stay on the server.
default) straight from the source tree. It is for developing the framework
itself; a room's `cfg/config.json` does not exist there, so the landing pages
fall back to their defaults. Rooms use docker.
## Diagrams
The `.dot` sources under `docs/graphs/` carry structure; the palette lives in
`docs/graphs/themes/*.gvpr` and is applied at render time, so one source renders
in every theme.
```bash
make docs graphs # every graph, every theme
make docs graphs lucid # one theme
```
`<name>.svg` is the dark default the docs link to; other themes write
`<name>.<theme>.svg`. See [Themes](#themes).

110
docs/data/en/themes.md Normal file
View File

@@ -0,0 +1,110 @@
# Themes
Three themes ship, and the same palettes drive the diagrams as well as the
pages. `soleprint` is the default; the others are switched to on purpose.
| Theme | Reads as | For |
| --- | --- | --- |
| `soleprint` | dark, rounded, amber | the default — tools and dev chrome |
| `mcrn` | dark, square, monospace, orange glow | the terminal look, matched to mariano.mcrn.ar |
| `lucid` | light, hairline, printable | regulated documents, and sitting beside a real lucid.app export |
Switch with `?theme=lucid`, or the toggle in the corner. The choice persists.
## Where it lives
```
soleprint/common/theme/
tokens.css the vocabulary + neutral defaults
themes/*.css one file per theme
theme.js resolve, apply, remember
bake.py inline the defaults into pages
```
Served together at `/theme.css` — tokens first, then every theme, each scoped to
`[data-theme="..."]`. Adding a theme is adding a file: `run.py` lists the
directory rather than carrying a hardcoded list.
## Two naming families, one set of values
Both are answered, because both were already in use and renaming across a dozen
templates would have been the larger change:
- `--bg` / `--surface` / `--border` / `--text` / `--muted` / `--accent` — the
station tools and the docs site
- `--surface-0..3` / `--text-primary` / `--panel-radius``common/ui`'s Vue
components
The second family is derived from the first in `tokens.css`, so a page using
either name gets the same colour and a theme author fills in one set.
## Order matters
```html
<!-- baked defaults --> <style>:root { }</style>
<link rel="stylesheet" href="/theme.css">
<style> the page's own rules </style>
```
The theme has to load **before** the page's styles, so its element defaults
underpin the page rather than override it. Get this backwards and
`tokens.css`'s `body { background: var(--bg) }` flattens whatever the page
wanted — which is exactly how artery, atlas and station briefly lost their
coloured content columns.
## Baked defaults, and why
`/theme.css` is an absolute path, and soleprint is not always at the root — in a
room's nginx it sits under `/spr/` while `location /` goes to the frontend. A
page that says `background: var(--bg)` and never receives `--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.
So every page carries a generated `:root` block **before** the link. Both are
`:root`, so document order decides: the served stylesheet wins when it loads,
and the baked block is what is left when it does not.
```bash
python3 common/theme/bake.py # regenerate
python3 common/theme/bake.py --check # fail if a page is stale
```
Only the variables a page actually uses are emitted, so the blocks stay small.
## No webfonts
The stacks name faces that exist on the target rather than fetching any:
```css
--font-ui: "Segoe UI", Inter, system-ui, -apple-system, Arial, sans-serif;
--font-mono: "Cascadia Mono", "JetBrains Mono", Consolas, "SF Mono", monospace;
```
Segoe UI and Consolas ship with Windows. A regulated network blocks
`fonts.googleapis.com` and `file://` stalls on it, and neither failure looks
like a missing font — they look like a broken page.
## Diagrams follow
`docs/graphs/themes/*.gvpr` carry the same palettes for graphviz, so a diagram
and the page around it are one visual language. See
[the graphs README](https://git.mcrn.ar/mariano/soleprint/src/branch/main/docs/graphs/README.md)
and [Export / Compile](#export).
```bash
make docs graphs # every graph, every theme
```
Diagrams are baked per theme rather than styled by CSS, because the docs embed
them with `<img src=…>` — which makes the SVG a separate document the page's
stylesheet cannot reach. A plexus that **inlines** the SVG can style it live,
and the bundle plexus does exactly that.
## Contrast
`lucid` is the first light theme, and the palette was measured rather than
guessed — against both `#ffffff` and the `#f5f7fa` panel, at the sizes actually
used. `--dim` drives 11px notes and `--status-warn` drives 10px labels, so both
need 4.5:1 rather than the 3:1 large text gets away with. The obvious lighter
greys came in at 3.43.8 and were dropped for that reason.

View File

@@ -1,32 +1,188 @@
[
{"id": "intro", "title": {"en": "Introduction"}},
{"id": "quickstart", "title": {"en": "Quick Start"}},
{"id": "concepts", "title": {"en": "Concepts"}},
{"id": "room-setup", "title": {"en": "↳ Room Setup"}, "sub": true},
{"id": "standalone", "title": {"en": "↳ Standalone"}, "sub": true},
{"id": "managed", "title": {"en": "↳ Managed"}, "sub": true},
{"id": "artery", "title": {"en": "Artery"}},
{"id": "artery-jira", "title": {"en": "↳ Jira"}, "sub": true},
{"id": "artery-google", "title": {"en": "↳ Google"}, "sub": true},
{"id": "artery-slack", "title": {"en": "↳ Slack"}, "sub": true},
{"id": "artery-ia", "title": {"en": "↳ IA"}, "sub": true},
{"id": "artery-shunts", "title": {"en": "↳ Shunts"}, "sub": true},
{"id": "atlas", "title": {"en": "Atlas"}},
{"id": "atlas-books", "title": {"en": "↳ Books"}, "sub": true},
{"id": "atlas-templates", "title": {"en": "↳ Templates"}, "sub": true},
{"id": "station", "title": {"en": "Station"}},
{"id": "station-tester", "title": {"en": "↳ Tester"}, "sub": true},
{"id": "station-datagen", "title": {"en": "↳ Datagen"}, "sub": true},
{"id": "station-modelgen", "title": {"en": "↳ Modelgen"}, "sub": true},
{"id": "station-graphgen", "title": {"en": "↳ Graphgen"}, "sub": true},
{"id": "station-shuntgen", "title": {"en": "↳ Shuntgen"}, "sub": true},
{"id": "station-databrowse", "title": {"en": "↳ Databrowse"}, "sub": true},
{"id": "station-cabinets", "title": {"en": "↳ Cabinets"}, "sub": true},
{"id": "components", "title": {"en": "Shared Components"}},
{"id": "export", "title": {"en": "Export / Compile"}},
{"id": "deployment", "title": {"en": "Deployment"}}
{
"id": "intro",
"title": {
"en": "Introduction"
}
},
{
"id": "quickstart",
"title": {
"en": "Quick Start"
}
},
{
"id": "concepts",
"title": {
"en": "Concepts"
}
},
{
"id": "room-setup",
"title": {
"en": "↳ Room Setup"
},
"sub": true
},
{
"id": "standalone",
"title": {
"en": "↳ Standalone"
},
"sub": true
},
{
"id": "managed",
"title": {
"en": "↳ Managed"
},
"sub": true
},
{
"id": "artery",
"title": {
"en": "Artery"
}
},
{
"id": "artery-jira",
"title": {
"en": "↳ Jira"
},
"sub": true
},
{
"id": "artery-google",
"title": {
"en": "↳ Google"
},
"sub": true
},
{
"id": "artery-slack",
"title": {
"en": "↳ Slack"
},
"sub": true
},
{
"id": "artery-ia",
"title": {
"en": "↳ IA"
},
"sub": true
},
{
"id": "artery-shunts",
"title": {
"en": "↳ Shunts"
},
"sub": true
},
{
"id": "artery-plexuses",
"title": {
"en": "↳ Plexuses"
},
"sub": true
},
{
"id": "atlas",
"title": {
"en": "Atlas"
}
},
{
"id": "atlas-books",
"title": {
"en": "↳ Books"
},
"sub": true
},
{
"id": "atlas-templates",
"title": {
"en": "↳ Templates"
},
"sub": true
},
{
"id": "station",
"title": {
"en": "Station"
}
},
{
"id": "station-tester",
"title": {
"en": "↳ Tester"
},
"sub": true
},
{
"id": "station-datagen",
"title": {
"en": "↳ Datagen"
},
"sub": true
},
{
"id": "station-modelgen",
"title": {
"en": "↳ Modelgen"
},
"sub": true
},
{
"id": "station-graphgen",
"title": {
"en": "↳ Graphgen"
},
"sub": true
},
{
"id": "station-shuntgen",
"title": {
"en": "↳ Shuntgen"
},
"sub": true
},
{
"id": "station-databrowse",
"title": {
"en": "↳ Databrowse"
},
"sub": true
},
{
"id": "station-cabinets",
"title": {
"en": "↳ Cabinets"
},
"sub": true
},
{
"id": "components",
"title": {
"en": "Shared Components"
}
},
{
"id": "export",
"title": {
"en": "Export / Compile"
}
},
{
"id": "themes",
"title": {
"en": "Themes"
}
},
{
"id": "deployment",
"title": {
"en": "Deployment"
}
}
]