228 lines
8.5 KiB
Markdown
228 lines
8.5 KiB
Markdown
# µFrame (Micronomicon)
|
|
|
|
A declarative DSL and web IDE for building rich terminal UIs that publish as `.mu` pages to [NomadNet](https://github.com/markqvist/NomadNet) — a decentralized communication platform on the [Reticulum](https://github.com/markqvist/Reticulum) mesh network.
|
|
|
|
Write structured layouts with box-drawing, gauges, tables, and forms in a simple DSL. Get both plain ASCII art (viewable in any terminal) and styled Micron markup (with colors, links, and interactive form fields) from the same source.
|
|
|
|
```
|
|
page "Dashboard" 60 ╔═ Relay Alpha ══════════════════╗
|
|
box double "Relay Alpha" ║ Reticulum Network Node ║
|
|
align center ╚════════════════════════════════╝
|
|
text "Reticulum Network Node"
|
|
CPU ████████████████░░░░ 62%
|
|
gauge "CPU" 62 100 28 warn=75 crit=90 MEM ██████████████████░░ 84% ⚠
|
|
gauge "MEM" 84 100 28 warn=80 crit=95
|
|
┌──────────┬──────┬──────────┐
|
|
table "Routes" │Dest │Hops │Status │
|
|
columns "Dest" 10 | "Hops" 6 | ... ├──────────┼──────┼──────────┤
|
|
row "east" | "2" | "alive" │east │2 │● alive │
|
|
└──────────┴──────┴──────────┘
|
|
status "East Relay" online ● East Relay
|
|
```
|
|
|
|
---
|
|
|
|
## Quick Start
|
|
|
|
### Web IDE
|
|
|
|
```bash
|
|
# 1. Create directories
|
|
mkdir -p ~/.nomadnetwork/storage/pages ~/.micron-editor/sources
|
|
|
|
# 2. Backend
|
|
cd backend
|
|
python -m venv .venv && source .venv/bin/activate
|
|
pip install -r requirements.txt
|
|
PAGES_DIR=~/.nomadnetwork/storage/pages \
|
|
SOURCES_DIR=~/.micron-editor/sources \
|
|
uvicorn main:app --reload --port 8080
|
|
|
|
# 3. Frontend (separate terminal)
|
|
cd frontend
|
|
npm install
|
|
npm run dev
|
|
```
|
|
|
|
Open http://localhost:5173 → **New Page** → **Examples** → pick a template.
|
|
|
|
### CLI
|
|
|
|
```bash
|
|
cd backend && source .venv/bin/activate
|
|
|
|
python -m uframe render page.uf # ASCII to stdout
|
|
python -m uframe render page.uf --micron # Micron to stdout
|
|
python -m uframe compile page.uf # → page.mu
|
|
python -m uframe check page.uf # validate
|
|
python -m uframe deploy page.uf # compile + copy to NomadNet pages
|
|
```
|
|
|
|
### Docker
|
|
|
|
```bash
|
|
# 1. Build the frontend
|
|
cd frontend
|
|
npm install
|
|
npm run build
|
|
cd ..
|
|
|
|
# 2. Build and start all services
|
|
docker compose up --build -d
|
|
```
|
|
|
|
This starts two containers:
|
|
|
|
- **micronomicon** — the web IDE + API on <http://localhost:8080>
|
|
- **nomadnet** — NomadNet node serving your published `.mu` pages
|
|
|
|
Pages published through the IDE are written to a shared volume that NomadNet reads from. The Docker socket is mounted read-only so the IDE can restart NomadNet when needed.
|
|
|
|
To follow logs: `docker compose logs -f`
|
|
|
|
To stop: `docker compose down`
|
|
|
|
To expose over Tailscale: `tailscale serve --bg https+insecure://localhost:8080`
|
|
|
|
#### Deploy a page via CLI (into the running stack)
|
|
|
|
```bash
|
|
cd backend && source .venv/bin/activate
|
|
python -m uframe deploy page.uf --dest "$(docker volume inspect micronomicon_pages -f '{{.Mountpoint}}')"
|
|
```
|
|
|
|
Or use the web IDE at <http://localhost:8080> and click **Publish**.
|
|
|
|
---
|
|
|
|
## DSL Overview
|
|
|
|
### Layout
|
|
```
|
|
page "Title" [width] # root container
|
|
box [light|heavy|double|rounded] "Title" # bordered panel
|
|
row [gap] # horizontal split
|
|
col [width] # column
|
|
spacer [lines] # vertical space
|
|
```
|
|
|
|
### Content
|
|
```
|
|
heading [1|2|3] "Text" # heading
|
|
text "Hello @bold{world} @color{0f0}{green}" # text with inline modifiers
|
|
label "Key" "Value" # key-value pair
|
|
link "Click me" "/page/dest.mu" # clickable link
|
|
divider [light|heavy|double] # horizontal rule
|
|
```
|
|
|
|
### Data Visualization
|
|
```
|
|
gauge "CPU" 62 100 28 warn=75 crit=90 # ████████░░░░ 62%
|
|
sparkline "Net" "1,3,5,8,7,5" 20 # ⣀⣤⣶⣿⣷⣤ braille chart
|
|
status "Server" [online|offline|degraded] # ●○◐ indicator
|
|
table "Routes"
|
|
columns "Dest" 20 | "Hops" 6
|
|
row "east" | "2"
|
|
```
|
|
|
|
### Forms
|
|
```
|
|
form "search"
|
|
field "query" 30 "Search..." # text input
|
|
radio "scope" "Local" | "Network" # radio buttons
|
|
checkbox "cache" "Include cached" # checkbox
|
|
button "Go" "/page/search.mu" # submit
|
|
```
|
|
|
|
### Dynamic Pages
|
|
```
|
|
cache 0 # re-execute on every request
|
|
source cpu : shell "cat /proc/loadavg" # live data
|
|
if $cpu > 90
|
|
text "ALERT"
|
|
for peer in $peers
|
|
status "$peer.name" $peer.state
|
|
state "visits" "/tmp/visits.json" # persistent store
|
|
```
|
|
|
|
### Components
|
|
```
|
|
component stat(label, value, max)
|
|
gauge "$label" $value $max 20
|
|
|
|
stat "CPU" 62 100 # reuse
|
|
use std/dashboard # import standard library
|
|
banner "My Node" "Mesh Network" # use library component
|
|
```
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
```
|
|
.uf source → Parse → IR Tree → Measure → Layout → Paint → CharGrid
|
|
├→ ASCII (plain text)
|
|
├→ Micron (.mu with styles)
|
|
└→ Script (dynamic: executable Python)
|
|
```
|
|
|
|
**Static pages**: `.uf` compiles to `.mu` (Micron markup). NomadNet serves the file directly.
|
|
|
|
**Dynamic pages**: `.uf` with `source`/`if`/`for` compiles to an executable Python script. NomadNet detects the `+x` bit, runs the script on each request, and serves the stdout as Micron. Live system data, form handling, and state persistence all work through this model.
|
|
|
|
---
|
|
|
|
## API
|
|
|
|
| Method | Path | Description |
|
|
|--------|---------------------|-------------------------------------------------------|
|
|
| POST | /api/compile | Compile `.uf` → `{ascii, micron, script, is_dynamic}` |
|
|
| GET | /api/pages | List all pages with metadata |
|
|
| GET | /api/pages/{name} | Read page source |
|
|
| POST | /api/pages/{name} | Save `{source, publish}` — draft or publish |
|
|
| DELETE | /api/pages/{name} | Delete page |
|
|
| GET | /api/graph | Page link graph |
|
|
| POST | /api/restart | Restart NomadNet container |
|
|
|
|
---
|
|
|
|
## Web IDE Features
|
|
|
|
- **Split-pane editor** with µFrame syntax highlighting and live preview
|
|
- **`/` command palette** — type `/` to insert any DSL primitive
|
|
- **4 preview tabs** — ASCII | Micron (rendered) | Raw | Script (dynamic only)
|
|
- **Examples dropdown** — 9 templates from Hello World to Dynamic Dashboard
|
|
- **Pages dashboard** with Published / Draft / Orphan status badges
|
|
- **Page graph** — React Flow visualization of inter-page links
|
|
- **Keyboard shortcuts** — `Ctrl+S` save, `Ctrl+P` publish
|
|
- **Backlink indicator** — shows which pages link to the current page
|
|
|
|
---
|
|
|
|
## Environment Variables
|
|
|
|
| Variable | Default | Description |
|
|
|----------------------|------------------|--------------------------------------|
|
|
| `PAGES_DIR` | `/data/pages` | NomadNet pages directory |
|
|
| `SOURCES_DIR` | `/data/sources` | µFrame source files directory |
|
|
| `NOMADNET_CONTAINER` | `nomadnet` | Docker container name to restart |
|
|
|
|
---
|
|
|
|
## Tests
|
|
|
|
```bash
|
|
cd backend && source .venv/bin/activate
|
|
python -m pytest uframe/tests/ -v # 91 tests
|
|
```
|
|
|
|
---
|
|
|
|
## References
|
|
|
|
- [NomadNet](https://github.com/markqvist/NomadNet) — decentralized communication
|
|
- [Reticulum](https://github.com/markqvist/Reticulum) — mesh networking stack
|
|
- [Micron syntax](https://github.com/fr33n0w/micron-composer) — markup reference
|
|
- [Design spec](docs/framework-design-v3.md) — full DSL design document
|
|
- [Dynamic templates](docs/dynamic-templates.md) — dynamic page system spec
|