feat: preview
This commit is contained in:
281
README.md
281
README.md
@@ -1,71 +1,175 @@
|
||||
# Micronomicon
|
||||
# µFrame (Micronomicon)
|
||||
|
||||
A self-hosted web editor for writing Markdown and publishing `.mu` pages to a NomadNet node.
|
||||
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 in Markdown → preview as Micron → publish directly to `~/.nomadnetwork/storage/pages/`.
|
||||
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
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
## Quick Start
|
||||
|
||||
- Docker + Docker Compose
|
||||
- Python 3.13+ (for local backend development only)
|
||||
- Node 20+ (for local frontend development only)
|
||||
- A running NomadNet container named `nomadnet` (for the restart button)
|
||||
|
||||
---
|
||||
|
||||
## Quick Start (Docker)
|
||||
### Web IDE
|
||||
|
||||
```bash
|
||||
# 1. Build the frontend
|
||||
cd frontend
|
||||
npm install
|
||||
npm run build
|
||||
cd ..
|
||||
|
||||
# 2. Create source directories
|
||||
# 1. Create directories
|
||||
mkdir -p ~/.nomadnetwork/storage/pages ~/.micron-editor/sources
|
||||
|
||||
# 3. Start the stack
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
App is available at `http://localhost:8080`.
|
||||
|
||||
### Tailscale HTTPS
|
||||
|
||||
```bash
|
||||
tailscale serve --bg https+insecure://localhost:8080
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Local Development
|
||||
|
||||
Run backend and frontend separately with hot reload.
|
||||
|
||||
**Backend**
|
||||
|
||||
```bash
|
||||
# 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
|
||||
```
|
||||
|
||||
**Frontend**
|
||||
|
||||
```bash
|
||||
# 3. Frontend (separate terminal)
|
||||
cd frontend
|
||||
npm install
|
||||
npm run dev # proxies /api → localhost:8080
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Open `http://localhost:5173`.
|
||||
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
|
||||
cd frontend && npm install && npm run build && cd ..
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
App at http://localhost:8080. Add Tailscale: `tailscale serve --bg https+insecure://localhost:8080`
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
|
||||
---
|
||||
|
||||
@@ -74,85 +178,24 @@ Open `http://localhost:5173`.
|
||||
| Variable | Default | Description |
|
||||
|----------------------|------------------|--------------------------------------|
|
||||
| `PAGES_DIR` | `/data/pages` | NomadNet pages directory |
|
||||
| `SOURCES_DIR` | `/data/sources` | Markdown source files directory |
|
||||
| `SOURCES_DIR` | `/data/sources` | µFrame source files directory |
|
||||
| `NOMADNET_CONTAINER` | `nomadnet` | Docker container name to restart |
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
## Tests
|
||||
|
||||
| Method | Path | Description |
|
||||
|----------|---------------------|-----------------------------------------------|
|
||||
| `GET` | `/api/health` | Health check |
|
||||
| `POST` | `/api/convert` | Convert `{ markdown }` → `{ micron }` |
|
||||
| `GET` | `/api/pages` | List all pages with metadata |
|
||||
| `GET` | `/api/pages/{name}` | Read page (markdown source + micron output) |
|
||||
| `POST` | `/api/pages/{name}` | Save `{ markdown, publish }` — draft or live |
|
||||
| `DELETE` | `/api/pages/{name}` | Delete source and/or `.mu` file |
|
||||
| `GET` | `/api/graph` | Graph nodes + edges from parsed link sources |
|
||||
| `POST` | `/api/restart` | Restart NomadNet Docker container |
|
||||
|
||||
---
|
||||
|
||||
## Directory Layout
|
||||
|
||||
```
|
||||
micronomicon/
|
||||
Dockerfile
|
||||
compose.yml
|
||||
backend/
|
||||
main.py ← FastAPI app + static file serving
|
||||
converter.py ← md2txt wrapper (POST /api/convert)
|
||||
pages.py ← file management (CRUD /api/pages)
|
||||
graph.py ← link parser (GET /api/graph)
|
||||
docker_utils.py ← container restart (POST /api/restart)
|
||||
requirements.txt
|
||||
frontend/
|
||||
src/
|
||||
App.tsx
|
||||
routes/ ← DashboardView, EditorView, GraphView
|
||||
components/ ← dashboard/, editor/, shared/, ui/ (shadcn)
|
||||
stores/ ← editorStore, pagesStore (Zustand)
|
||||
hooks/ ← useConversion, useGraph, useUnsavedGuard
|
||||
lib/ ← utils (cn)
|
||||
|
||||
~/.nomadnetwork/storage/pages/ ← published .mu files (NomadNet serves these)
|
||||
~/.micron-editor/sources/ ← markdown sources (managed by this app)
|
||||
```bash
|
||||
cd backend && source .venv/bin/activate
|
||||
python -m pytest uframe/tests/ -v # 42 tests
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Page Lifecycle
|
||||
## References
|
||||
|
||||
```
|
||||
New Page → /editor/new → Save Draft → .md saved to sources/
|
||||
→ Publish → .md saved + .mu written to pages/
|
||||
```
|
||||
|
||||
- **Draft** — `.md` exists, no `.mu`. Not visible on NomadNet.
|
||||
- **Published** — both `.md` and `.mu` exist.
|
||||
- **Orphan** — `.mu` exists but no `.md` source (e.g. pages created outside this tool).
|
||||
|
||||
---
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Layer | Technology |
|
||||
|-----------|-----------------------------------------|
|
||||
| Backend | Python 3.13 + FastAPI + uvicorn |
|
||||
| Converter | md2txt (micron renderer) |
|
||||
| Frontend | React 19 + Vite + TypeScript |
|
||||
| UI | shadcn/ui + Tailwind CSS v4 |
|
||||
| Editor | CodeMirror 6 |
|
||||
| Graph | React Flow + dagre |
|
||||
| State | Zustand |
|
||||
| Container | Docker + Compose |
|
||||
|
||||
---
|
||||
|
||||
## Known Limitations (Phase 1)
|
||||
|
||||
- Micron preview is plain text — full terminal rendering comes in a later phase (micron-parser-js iframe)
|
||||
- `[[` link autocomplete not yet implemented (Phase 2)
|
||||
- Graph view is read-only; click a node to open it in the editor
|
||||
- No metrics (Phase 4)
|
||||
- [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
|
||||
|
||||
Reference in New Issue
Block a user