# µFrame (Micronomicon)

A self-hosted web IDE and CLI for building rich terminal UIs using a declarative DSL. Compiles `.uf` source files into both plain ASCII art and styled Micron `.mu` pages for NomadNet — a decentralized communication platform running on the Reticulum mesh network.

## What It Does

Write this:
```
page "Node Status" 60
  box double "Relay Alpha-7"
    align center
    text "Reticulum Network Node"
  gauge "CPU" 62 100 28 warn=75 crit=90
  status "East Relay" online
  table "Routes"
    columns "Dest" 20 | "Hops" 6 | "Status" 10
    row "relay-east" | "2" | "@color{0f0}{alive}"
```

Get this (ASCII):
```
╔═ Relay Alpha-7 ════════════════════════════════════════════╗
║               Reticulum Network Node                       ║
╚════════════════════════════════════════════════════════════╝
CPU  ████████████████████░░░░░░░░  62%
● East Relay
┌────────────────────┬──────┬──────────┐
│Dest                │Hops  │Status    │
├────────────────────┼──────┼──────────┤
│relay-east          │2     │● alive   │
└────────────────────┴──────┴──────────┘
```

And the same content as Micron `.mu` with color tags, bold, links, and interactive form fields — ready to serve on NomadNet.

## Quick Start

### Web IDE

```bash
# Backend
cd backend
source .venv/bin/activate
PAGES_DIR=~/.nomadnetwork/storage/pages \
SOURCES_DIR=~/.micron-editor/sources \
uvicorn main:app --reload --port 8080

# Frontend (separate terminal)
cd frontend
npm run dev
```

Open http://localhost:5173 → click **New Page** → click **Examples** → pick a template.

### CLI

```bash
cd backend
source .venv/bin/activate

# Render to terminal
python -m uframe render page.uf

# Render Micron only
python -m uframe render page.uf --micron

# Compile to .mu file
python -m uframe compile page.uf --out page.mu

# Validate without output
python -m uframe check page.uf

# Compile and deploy to NomadNet
python -m uframe deploy page.uf
```

## Tech Stack

| Layer      | Technology                                     |
|------------|-------------------------------------------------|
| Backend    | Python 3.13 + FastAPI + uvicorn                |
| µFrame     | Pure Python (zero deps): parser → IR → CharGrid → emitters |
| Frontend   | React 19 + Vite + TypeScript                   |
| UI         | shadcn/ui + Tailwind CSS v4                    |
| Editor     | CodeMirror 6 (custom µFrame syntax mode)       |
| Graph      | React Flow (@xyflow/react) + dagre             |
| State      | Zustand                                        |
| Container  | Docker + Compose                               |

## Project Structure

```
micronomicon/
├── backend/
│   ├── main.py              # FastAPI app + static file serving
│   ├── converter.py          # POST /api/compile endpoint
│   ├── pages.py              # CRUD /api/pages (.uf sources + .mu publish)
│   ├── graph.py              # GET /api/graph (link parser)
│   ├── docker_utils.py       # POST /api/restart (NomadNet container)
│   └── uframe/               # µFrame engine (16 modules, ~3800 LOC)
│       ├── __init__.py        #   compile(source, width) → CompileResult
│       ├── parser.py          #   .uf DSL → IR tree (indentation-based)
│       ├── ir.py              #   30+ IR node dataclasses
│       ├── grid.py            #   CharGrid — 2D char + style buffer
│       ├── chars.py           #   Unicode tables (box-drawing, braille)
│       ├── measure.py         #   Bottom-up size computation
│       ├── layout.py          #   Top-down position assignment
│       ├── paint.py           #   IR nodes → CharGrid rendering
│       ├── borders.py         #   Junction merging post-pass
│       ├── emit_ascii.py      #   CharGrid → plain text
│       ├── emit_micron.py     #   CharGrid → Micron with style tags
│       ├── codegen.py         #   Dynamic page → executable Python script
│       ├── cli.py             #   CLI: render / compile / check / deploy
│       ├── errors.py          #   ParseError, LayoutError, CompileWarning
│       └── tests/             #   42 tests (compile, dynamic, components)
├── frontend/src/
│   ├── routes/                # DashboardView, EditorView, GraphView
│   ├── components/editor/
│   │   ├── EditorPane.tsx     #   CodeMirror 6 host
│   │   ├── PreviewPane.tsx    #   ASCII / Micron / Raw / Script tabs
│   │   ├── ToolBar.tsx        #   Save, Publish, Examples, Backlinks
│   │   ├── uframeHighlight.ts #   µFrame syntax highlighting
│   │   ├── uframeCommands.ts  #   "/" slash command palette
│   │   ├── micronRenderer.ts  #   Micron → HTML preview renderer
│   │   └── examples.ts        #   9 built-in example templates
│   ├── hooks/
│   │   ├── useCompile.ts      #   Debounced POST /api/compile
│   │   └── useUnsavedGuard.ts #   Prevent accidental navigation
│   └── stores/
│       ├── editorStore.ts     #   Zustand: source, compiled output, preview mode
│       └── pagesStore.ts      #   Zustand: page list, delete, unpublish
└── docs/
    ├── framework-design-v3.md #   Full DSL spec + rendering model
    └── dynamic-templates.md   #   Dynamic page addendum
```

## API Endpoints

| Method | Path                | Description                                          |
|--------|---------------------|------------------------------------------------------|
| GET    | /api/health         | Health check                                         |
| 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 page — `{ source, publish }`                    |
| DELETE | /api/pages/{name}   | Delete source and/or .mu file                        |
| GET    | /api/graph          | Page link graph (nodes + edges)                      |
| POST   | /api/restart        | Restart NomadNet Docker container                    |

## Storage

```
~/.micron-editor/sources/       ← .uf source files (drafts + published)
~/.nomadnetwork/storage/pages/  ← Compiled .mu files served by NomadNet
```

- **Save Draft**: writes `.uf` to sources dir only
- **Publish (static)**: compiles `.uf` → `.mu`, writes to pages dir (chmod 644)
- **Publish (dynamic)**: compiles `.uf` → executable Python script, writes to pages dir (chmod 755)

NomadNet auto-detects the execute bit: static pages are served as-is, dynamic pages are executed and their stdout is served.

## µFrame DSL Reference

### Layout
```
page "Title" [width]                         # root (default width 64)
  box [light|heavy|double|rounded] "Title"   # bordered panel
  row [gap]                                  # horizontal layout
    col [width]                              # column in a row
  spacer [lines]                             # vertical whitespace
  pad [t] [r] [b] [l]                       # inner margin
```

### Content
```
heading [1|2|3] "Text"                       # styled heading
text "Content with @bold{inline} @color{hex}{modifiers}"
label "Key" "Value"                          # aligned key-value pair
list [bullet|dash]
  item "Entry"
link "Display text" "/dest.mu"               # clickable in Micron
divider [light|heavy|double|dash|dot]        # horizontal rule
# comment                                   # ignored in output
```

### Data Visualization
```
gauge "Label" value max width [warn=N crit=N]  # ████░░░░ bar with thresholds
sparkline "Label" "1,3,5,8,7,5" width          # ⣀⣤⣶⣿⣷⣤ braille chart
status "Label" [online|offline|degraded]        # ●○◐ colored indicators
table "Title"
  columns "Name" 20 | "Hops" 6 | "Status" 10
  row "relay" | "2" | "@color{0f0}{● alive}"
```

### Forms
```
form "name"
  field "name" [width] "placeholder"          # text input
  password "name" [width] "placeholder"       # masked input
  radio "group" "Opt A" | "Opt B" | "Opt C"  # radio buttons
  checkbox "name" "Label"                     # checkbox
  button "Label" "/action/path"               # submit link
```

### Dynamic Features
```
cache 0                                       # never cache (re-execute)
source cpu : shell "cat /proc/loadavg"        # live data at render time
source config : json "/path/config.json"      # JSON file read
source ts : python "datetime.now().isoformat()" # Python expression
let name = "Relay Alpha"                      # variable assignment

if $cpu > 90
  text "ALERT: CPU critical"
elif $cpu > 75
  text "Warning: elevated"

for peer in $peers
  status "$peer.name" $peer.state

on_submit "search"
  source results : shell "search.py '$query'"
  text "$results"

state "counter" "/tmp/counter.json"           # persistent JSON store
```

### Components
```
# Define a reusable component
component stat(label, value, max)
  gauge "$label" $value $max 20

# Use it
stat "CPU" 62 100
stat "MEM" 84 100

# Import standard library
use std/dashboard
banner "My Node" "Mesh Network"
resources 62 84
```

### Standard Libraries
| Library | Components |
|---------|-----------|
| `std/dashboard` | `banner(title, subtitle)`, `resources(cpu, mem)`, `peer_status(name, state)` |
| `std/status-bar` | `status_bar(label, value, max)`, `status_item(name, state)` |
| `std/nav` | `nav_link(label, dest)`, `nav_divider()` |

## Rendering Pipeline

```
.uf source
    │
    ▼
  Parse ──→ IR Tree (30+ node types)
    │
    ▼
  Measure (bottom-up: compute sizes)
    │
    ▼
  Layout (top-down: assign positions)
    │
    ▼
  Paint (depth-first: write chars into CharGrid)
    │
    ▼
  Merge Borders (fix junction characters)
    │
    ├──→ ASCII emitter → plain text
    ├──→ Micron emitter → styled .mu (with colors, links, form tags)
    └──→ Codegen (if dynamic) → executable Python script
```

## Web IDE Features

- **Split-pane editor**: µFrame DSL source (left) / live preview (right)
- **Syntax highlighting**: keywords, strings, variables, comments in distinct colors
- **`/` command palette**: type `/` to insert layout, content, data viz, form, and style primitives
- **4 preview tabs**: ASCII | Micron (rendered) | Raw (Micron source) | Script (dynamic pages only)
- **`⚡ dynamic` badge**: auto-detected when source contains `source`, `if`, `for`, etc.
- **Examples dropdown**: 9 built-in templates (Hello World → Full Node Page → Dynamic Dashboard)
- **Pages dashboard**: table view with Published/Draft/Orphan status badges
- **Page graph**: React Flow visualization of inter-page links
- **Keyboard shortcuts**: `Ctrl+S` save draft, `Ctrl+P` publish
- **Unsaved changes guard**: warns before navigating away
- **Backlink indicator**: shows which pages link to the current page

## Running Tests

```bash
cd backend
source .venv/bin/activate
python -m pytest uframe/tests/ -v
```

42 tests covering:
- Static compilation (boxes, headings, text, gauges, tables, links, lists, spacers, dividers)
- Form elements (field, radio, checkbox, button)
- Dynamic pages (source, if/for, let, state, on_submit, codegen)
- Components (inline definitions, standard library, parameter substitution)

## Conventions

- µFrame engine is **pure Python stdlib** — zero external dependencies
- Backend is FastAPI; no ORM, flat file storage
- Frontend uses shadcn/ui components in `frontend/src/components/ui/`
- Feature components grouped by domain: `dashboard/`, `editor/`, `shared/`
- State management via Zustand stores
- All `.uf` sources stored in `SOURCES_DIR`, compiled `.mu` in `PAGES_DIR`

## References

- µFrame design spec: `docs/framework-design-v3.md`
- Dynamic templates spec: `docs/dynamic-templates.md`
- NomadNet: https://github.com/markqvist/NomadNet
- Micron syntax: https://github.com/fr33n0w/micron-composer
- Reticulum: https://github.com/markqvist/Reticulum
