# µ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
