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