# µFrame Dynamic Templates — Making Rich UIs Live on NomadNet

## Addendum to the µFrame Design Document (v3)

---

## 1. The NomadNet Dynamic Page Model

Understanding how NomadNet serves dynamic pages is essential to
understanding how µFrame templates become live applications.

### How it works

NomadNet has a simple but powerful execution model, analogous to
CGI on the early web:

```
  Client                             Node Server
  ──────                             ───────────
  1. Browse to /page/dashboard.mu
                        ───────────▶
                                     2. Is dashboard.mu executable?
                                        YES → run it
                                        NO  → send contents as-is

                                     3. Execute: #!/usr/bin/env python3
                                        Script prints Micron to stdout

                                     4. Capture stdout → send to client
                        ◀───────────
  5. Render Micron in terminal
```

Key mechanics:

- A `.mu` file without the execute bit is **static** — NomadNet
  sends its contents directly to the browsing client
- A `.mu` file with the execute bit set is **dynamic** — NomadNet
  runs it as a subprocess and serves whatever it prints to stdout
- The shebang line (`#!/usr/bin/env python3`) determines the
  interpreter — Python, Bash, Lua, Rust, anything that runs
- The script must **terminate** — it cannot wait for input or
  run indefinitely
- Cache behavior is controlled via a header line: `#!c=0` means
  never cache (always re-execute), `#!c=300` means cache for 5 min

### Form data flow

Micron form fields (`\`<field\`placeholder>`, `\`<^|group|val\`label>`,
etc.) collect user input. When the user clicks a link on a page
that contains form fields, the field data is submitted along with
the link request:

```
  Page A (has form fields + a submit link)

  ┌───────────────────────────────────────────────┐
  │  Name: `<24|name`Enter name...>               │
  │  Role: `<^|role|admin`Admin> `<^|role|user`User> │
  │                                               │
  │        `[Submit`:/page/handle.mu]              │
  └───────────────────────────────────────────────┘

  User fills in "Alice", selects "Admin", clicks Submit
                            │
                            ▼
  Page B (handle.mu) — receives field data via environment
  variables, generates a response page with Micron output
```

The submitted field data is passed to the executable script
through environment variables in the format:

```
  FIELD_name=Alice
  FIELD_role=admin
```

Or via stdin as a URL-encoded or structured data payload
(implementation varies by NomadNet version). The executable
script reads these values and uses them to generate its output.

---

## 2. µFrame's Dynamic Compilation Model

Here is the key insight: **µFrame doesn't just emit static
Micron text — it can compile `.uf` templates into executable
Python scripts that generate Micron at request time.**

This gives us three output modes:

```
  .uf source ──▶ Parser ──▶ IR ──┬──▶ Plain ASCII    (static preview)
                                 ├──▶ Static .mu      (static page)
                                 └──▶ Dynamic .mu     (executable script)
                                       │
                                       ▼
                                 #!/usr/bin/env python3
                                 # Auto-generated by µFrame
                                 # from: dashboard.uf
                                 import os, sys, json, subprocess
                                 ...
                                 print(rendered_micron)
```

The dynamic output is a self-contained Python script with:
- The µFrame rendering engine embedded (or imported)
- Data-fetching hooks that run at request time
- Form field processing from environment variables
- Conditional rendering based on submitted data
- The full ASCII art + Micron generation pipeline

### The three modes compared

```
  ┌──────────────────────────────────────────────────────────────┐
  │                     µFrame Output Modes                      │
  ├────────────────┬───────────────┬─────────────────────────────┤
  │ Plain ASCII    │ Static .mu    │ Dynamic .mu                 │
  ├────────────────┼───────────────┼─────────────────────────────┤
  │ Box-drawing    │ Box-drawing   │ Box-drawing                 │
  │ Block chars    │ Block chars   │ Block chars                 │
  │ Braille        │ Braille       │ Braille                     │
  │                │ + Color       │ + Color                     │
  │                │ + Bold/italic │ + Bold/italic               │
  │                │ + Links       │ + Links                     │
  │                │ + Form fields │ + LIVE form fields           │
  │                │               │ + Server-side data binding   │
  │                │               │ + Conditional rendering      │
  │                │               │ + Form submission handling   │
  │                │               │ + System data at render time │
  │                │               │ + State persistence          │
  ├────────────────┼───────────────┼─────────────────────────────┤
  │ Local terminal │ NomadNet page │ NomadNet live application   │
  │ preview        │ (cached)      │ (re-executed per request)   │
  └────────────────┴───────────────┴─────────────────────────────┘
```

---

## 3. DSL Extensions for Dynamic Behavior

### 3.1 Data Sources — `source` blocks

A `source` block declares where live data comes from. At render
time, the generated script executes the source and binds the
result to a variable.

```
# Shell command — output captured as string
source cpu_pct : shell "grep 'cpu ' /proc/stat | awk '{print int(($2+$4)*100/($2+$4+$5))}'"
source mem_pct : shell "free | awk '/Mem/{print int($3/$2*100)}'"
source uptime  : shell "uptime -p"
source peers   : shell "rnstatus -j | python3 -c 'import sys,json; d=json.load(sys.stdin); print(len(d.get(\"peers\",[]))); '"

# File read — contents loaded as string or parsed as JSON
source motd    : file "/etc/motd"
source config  : json "/home/node/.nomadnetwork/config.json"

# Python expression — evaluated inline
source timestamp : python "datetime.now().strftime('%Y-%m-%d %H:%M')"
source rand_hex  : python "secrets.token_hex(4)"

# RNS/Reticulum API — direct integration
source peer_list : rns "peers"
source node_info : rns "identity"
```

Sources are resolved **at page render time** — every time a
client requests the page, the commands run fresh.

Usage in templates:

```
gauge "CPU" $cpu_pct 100 28 warn=75 crit=90
gauge "MEM" $mem_pct 100 28 warn=80 crit=95
label "Uptime" "$uptime"
label "Peers" "$peers active"
text "Last updated: $timestamp"
```

### 3.2 Form Handling — `on_submit` blocks

An `on_submit` block defines what happens when a form is
submitted. It receives field values and controls what the
page renders in response.

```
page "Search" 64

  form "search"
    field "query" 30 "Enter search term..."
    radio "scope" "Local" | "Network" | "All"
    button "Search" "/page/search.mu"

  on_submit "search"
    # $query and $scope are now populated from submitted form data
    source results : shell "search_index.py '$query' --scope '$scope'"

    heading 2 "Results for: $query"

    if $results
      text "$results"
    else
      text "No results found."
```

The generated Python script handles this as:

```python
#!/usr/bin/env python3
#!c=0
import os, subprocess

# Read submitted form data
query = os.environ.get("FIELD_query", "")
scope = os.environ.get("FIELD_scope", "Local")

if query:
    # Form was submitted — render results
    results = subprocess.check_output(
        ["search_index.py", query, "--scope", scope]
    ).decode().strip()
    # ... render results template with Micron ...
else:
    # No submission — render the form
    # ... render form template with Micron ...
```

### 3.3 Conditional Rendering — `if` / `else` / `elif`

```
source disk_pct : shell "df / | awk 'NR==2{print int($5)}'"

if $disk_pct > 90
  box heavy "DISK CRITICAL"
    color f00
    gauge "Disk" $disk_pct 100 40 crit=90
    text "@bold{@color{f00}{Immediate action required!}}"
elif $disk_pct > 75
  box light "Disk Warning"
    color ff0
    gauge "Disk" $disk_pct 100 40 warn=75
else
  gauge "Disk" $disk_pct 100 40
```

### 3.4 Iteration — `for` loops

```
source peer_json : shell "rnstatus --json-peers"

heading 2 "Active Peers"

for peer in $peer_json
  row 2
    col 30
      text "$peer.name"
    col 10
      status "$peer.name" $peer.state
    col 10
      text "$peer.latency"
```

The `for` construct works with JSON arrays or newline-delimited
text from shell commands.

### 3.5 State Persistence — `state` blocks

Since each page request is a fresh script execution, state must
be stored externally. µFrame provides a simple key-value store
backed by a JSON file on the node:

```
state "counter" "/tmp/uframe_counter.json"

# Read
let visits = $counter.visits || 0

# Write (increments on each page load)
set counter.visits = $visits + 1

text "This page has been viewed $counter.visits times."
```

For form-driven state (e.g., a guestbook):

```
state "guestbook" "/var/nomadnet/guestbook.json"

form "sign"
  field "name" 20 "Your name..."
  field "message" 40 "Your message..."
  button "Sign" "/page/guestbook.mu"

on_submit "sign"
  append guestbook.entries { name: $name, message: $message, time: $timestamp }
  text "@color{0f0}{Thanks, $name! Your message has been saved.}"

heading 2 "Guestbook ($guestbook.entries.length entries)"

for entry in $guestbook.entries
  box light
    text "@bold{$entry.name} — @italic{$entry.time}"
    text "$entry.message"
  spacer
```

### 3.6 Page Navigation with Data — `link` with parameters

Links can pass data to the target page via query-style encoding:

```
# Simple navigation
link "Home" "/page/index.mu"

# Navigation with parameters
link "View Peer $peer.name" "/page/peer_detail.mu?hash=$peer.hash"

# In the target page, access with:
source peer_hash : param "hash"
```

### 3.7 Cache Control

```
page "Dashboard" 64
  cache 0              # never cache — always re-execute
  # cache 60           # cache for 60 seconds
  # cache none         # alias for 0

  source cpu : shell "..."
  ...
```

Translates to the Micron header `#!c=0` in the first line of output.

---

## 4. Compilation Pipeline — .uf to Executable .mu

### 4.1 What the compiler generates

A `.uf` file with dynamic features compiles into a Python script
that:

1. Sets the shebang and cache header
2. Imports required modules
3. Reads environment variables (form data)
4. Executes source commands (shell, file, python, rns)
5. Evaluates conditionals and loops
6. Renders the IR tree into a CharGrid
7. Emits the CharGrid as Micron with style tags
8. Prints to stdout

```python
#!/usr/bin/env python3
#!c=0
# Auto-generated by µFrame from dashboard.uf
# Do not edit — regenerate with: uframe compile dashboard.uf

import os, sys, json, subprocess, datetime, secrets

# ─── µFrame Runtime (embedded) ───────────────────────────────

class CharGrid:
    """2D character grid with style annotations."""
    def __init__(self, width, height):
        self.w = width
        self.h = height
        self.chars = [[' ']*width for _ in range(height)]
        self.styles = [[None]*width for _ in range(height)]

    def put(self, x, y, ch, style=None):
        if 0 <= x < self.w and 0 <= y < self.h:
            self.chars[y][x] = ch
            self.styles[y][x] = style

    def box(self, x, y, w, h, weight='light', title=None, title_style=None):
        """Draw a box with automatic border characters."""
        # ... border drawing logic ...

    def gauge(self, x, y, w, value, max_val, label=None,
              warn=None, crit=None):
        """Render a horizontal gauge bar with threshold colors."""
        pct = min(value / max_val, 1.0)
        filled = int(w * pct)
        for i in range(w):
            ch = '█' if i < filled else '░'
            fg = None
            if crit and value >= crit: fg = 'f00'
            elif warn and value >= warn: fg = 'ff0'
            elif i < filled: fg = '0f0'
            else: fg = '555'
            self.put(x + i, y, ch, {'fg': fg})
        # ... label and percentage ...

    def sparkline(self, x, y, w, values):
        """Render braille sparkline from value array."""
        # ... braille pattern generation ...

    def emit_micron(self):
        """Scan grid and emit Micron with style tags."""
        lines = []
        for row_idx in range(self.h):
            line = []
            cur_style = None
            for col_idx in range(self.w):
                ch = self.chars[row_idx][col_idx]
                st = self.styles[row_idx][col_idx]
                if st != cur_style:
                    # Close previous style tags
                    if cur_style:
                        if cur_style.get('fg'): line.append('`f')
                        if cur_style.get('bold'): line.append('`!')
                    # Open new style tags
                    if st:
                        if st.get('bold'): line.append('`!')
                        if st.get('fg'): line.append(f'`F{st["fg"]}')
                    cur_style = st
                line.append(ch)
            # Close final style
            if cur_style:
                if cur_style.get('fg'): line.append('`f')
                if cur_style.get('bold'): line.append('`!')
            lines.append(''.join(line).rstrip())
        return '\n'.join(lines)

# ─── Form Data ───────────────────────────────────────────────

def get_field(name, default=''):
    """Read submitted form field from environment."""
    return os.environ.get(f'FIELD_{name}', default)

def get_param(name, default=''):
    """Read URL parameter."""
    return os.environ.get(f'PARAM_{name}',
           os.environ.get(f'var_{name}', default))

# ─── Data Sources ────────────────────────────────────────────

def shell(cmd):
    """Execute shell command, return stdout."""
    try:
        return subprocess.check_output(
            cmd, shell=True, timeout=5
        ).decode().strip()
    except Exception:
        return ''

# ─── Resolve Sources ─────────────────────────────────────────

cpu_pct = int(shell(
    "grep 'cpu ' /proc/stat | awk '{print int(($2+$4)*100/($2+$4+$5))}'"
) or 0)
mem_pct = int(shell(
    "free | awk '/Mem/{print int($3/$2*100)}'"
) or 0)
uptime_str = shell("uptime -p")
peer_count = shell("rnstatus -j 2>/dev/null | python3 -c "
    "'import sys,json; print(len(json.load(sys.stdin).get(\"peers\",[])))'")
timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M')

# ─── Build Grid & Render ────────────────────────────────────

grid = CharGrid(66, 40)

# ... all the box(), gauge(), sparkline(), text() calls
# ... exactly as the layout engine would produce them ...

# ─── Output ──────────────────────────────────────────────────

print('#!c=0')   # cache header: never cache
print(grid.emit_micron())
```

### 4.2 CLI usage

```bash
# Compile to dynamic executable .mu
uframe compile dashboard.uf --out dashboard.mu
chmod +x dashboard.mu

# Compile and deploy in one step
uframe deploy dashboard.uf
# → renders, sets +x, copies to ~/.nomadnetwork/storage/pages/

# Compile with embedded vs. imported runtime
uframe compile dashboard.uf --embed    # single self-contained file
uframe compile dashboard.uf --import   # requires uframe_runtime.py on node
```

### 4.3 Compilation modes

```
┌────────────────────────────────────────────────────────────────┐
│                   µFrame Compilation Modes                     │
├────────────────┬───────────────┬───────────────────────────────┤
│ uframe render  │ uframe render │ uframe compile                │
│   --ascii      │   --micron    │                               │
├────────────────┼───────────────┼───────────────────────────────┤
│ Static ASCII   │ Static .mu    │ Executable .mu (Python)       │
│ to stdout      │ file          │ file with +x                  │
├────────────────┼───────────────┼───────────────────────────────┤
│ All values     │ All values    │ source{} values fetched       │
│ resolved at    │ resolved at   │ at request time               │
│ render time    │ render time   │                               │
│                │               │ Form fields become live       │
│                │               │ on_submit{} blocks active     │
│                │               │ if/for evaluated per request  │
│                │               │ State persists across visits  │
└────────────────┴───────────────┴───────────────────────────────┘
```

---

## 5. Complete Dynamic Example

### 5.1 Source — `search_node.uf`

```
page "Node Search" 64
  cache 0

  source timestamp : python "datetime.now().strftime('%H:%M:%S')"
  source peer_count : shell "rnstatus 2>/dev/null | grep -c 'Peer'"
  state "history" "/var/nomadnet/search_history.json"

  box double "Node Search"
    align center
    text "Find peers and pages on the Reticulum mesh"
    text "@italic{$peer_count peers reachable · updated $timestamp}"

  spacer

  form "search"
    field "query" 30 "Search term..."
    radio "type" "Nodes" | "Pages" | "Files"
    checkbox "cache" "Include cached results"
    button "Search" "/page/search_node.mu"

  on_submit "search"
    # Log the search
    append history.queries { q: $query, type: $type, time: $timestamp }

    source results : shell "mesh_search.py '$query' --type '$type'"
    source result_count : python "len('''$results'''.strip().splitlines())"

    divider light

    heading 2 "Results for \"$query\" ($result_count found)"

    if $result_count > 0
      for line in $results
        source parts : python "'''$line'''.split('|')"
        row 1
          col 28
            link "$parts.0" "/page/detail.mu?hash=$parts.1"
          col 8
            text "$parts.2"
          col 10
            status "$parts.0" $parts.3
    else
      spacer
      text "@center{@color{ff0}{No results found for \"$query\"}}"
      spacer

    divider light
    heading 3 "Recent Searches"

    for entry in $history.queries[-5:]
      text "  $entry.time  $entry.q ($entry.type)"

  divider heavy
  text "@center{@italic{Relay Alpha-7 · $timestamp}}"
```

### 5.2 What the client sees

**Before submission** (form is empty):

```
╔══ Node Search ═══════════════════════════════════════════════╗
║            Find peers and pages on the Reticulum mesh        ║
║            7 peers reachable · updated 14:32:07              ║
╚══════════════════════════════════════════════════════════════╝

  Search: [ Search term...___________________ ]
  Type:   (•) Nodes   ( ) Pages   ( ) Files
          [ ] Include cached results

                            `[`!Search`!`:/page/search_node.mu]

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                   Relay Alpha-7 · 14:32:07
```

**After submitting "relay"** (script re-executes with FIELD_query=relay):

```
╔══ Node Search ═══════════════════════════════════════════════╗
║            Find peers and pages on the Reticulum mesh        ║
║            7 peers reachable · updated 14:32:15              ║
╚══════════════════════════════════════════════════════════════╝

  Search: [ relay__________________________ ]
  Type:   (•) Nodes   ( ) Pages   ( ) Files
          [ ] Include cached results

                            `[`!Search`!`:/page/search_node.mu]

  ──────────────────────────────────────────────────────────────

  >> Results for "relay" (3 found)

  `F0cfRelay-East`f             2 hops     `F0f0●`f online
  `F0cfRelay-South`f            4 hops     `F0f0●`f online
  `F0cfRelay-Backup`f           6 hops     `Fff0◐`f degraded

  ──────────────────────────────────────────────────────────────

  >>> Recent Searches
    14:32:15  relay (Nodes)
    14:28:44  bridge (Pages)
    14:25:01  firmware (Files)

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                   Relay Alpha-7 · 14:32:15
```

The form fields retain submitted values, results appear below,
and the search history persists across visits via the state file.
Every piece of box-drawing, every colored indicator, every braille
sparkline renders identically in both ASCII preview and live Micron.

---

## 6. Dynamic Patterns — A Cookbook

### 6.1 Live Dashboard (auto-refresh via cache=0)

```
page "Status" 64
  cache 0

  source cpu : shell "cat /proc/loadavg | awk '{print int($1*100/$(nproc))}'"
  source mem : shell "free | awk '/Mem/{print int($3/$2*100)}'"
  source net_in  : shell "net_traffic.sh in"
  source net_out : shell "net_traffic.sh out"
  source net_history_in  : shell "net_spark.sh in 20"
  source net_history_out : shell "net_spark.sh out 20"

  box heavy "System Status"
    row 2
      gauge "CPU" $cpu 100 28 warn=75 crit=90
      gauge "MEM" $mem 100 28 warn=80 crit=95
    spacer
    label "IN"  "$net_in KB/s"
    sparkline "IN" $net_history_in 28
    label "OUT" "$net_out KB/s"
    sparkline "OUT" $net_history_out 28

  text "@center{@italic{Press Ctrl+R to refresh}}"
```

Client hits the page → script runs → reads `/proc` → renders
gauges and sparklines with real data → client sees it.
Ctrl+R re-requests → fresh execution → updated values.

### 6.2 Guestbook with Persistent State

```
page "Guestbook" 64
  cache 0

  state "gb" "/var/nomadnet/guestbook.json"
  source timestamp : python "datetime.now().strftime('%Y-%m-%d %H:%M')"

  heading 1 "Guestbook"

  form "sign"
    field "name" 20 "Your name"
    field "msg" 40 "Leave a message..."
    button "Sign" "/page/guestbook.mu"

  on_submit "sign"
    if $name && $msg
      prepend gb.entries { name: $name, msg: $msg, time: $timestamp }
      text "@color{0f0}{✓ Thanks, $name!}"

  divider light

  for entry in $gb.entries[:20]
    box rounded
      text "@bold{$entry.name}  @italic{@color{888}{$entry.time}}"
      text "$entry.msg"
    spacer
```

### 6.3 Multi-Page Wizard with Navigation

```
# Page 1: setup.mu
page "Setup Wizard — Step 1" 64
  cache 0

  heading 1 "Network Configuration"
  form "net"
    field "interface" 20 "eth0"
    radio "mode" "Auto" | "Manual" | "Mesh Only"
    button "Next →" "/page/setup_2.mu"

# Page 2: setup_2.mu
page "Setup Wizard — Step 2" 64
  cache 0

  source iface : param "interface"   # or field from previous page
  source mode  : param "mode"

  heading 1 "Confirm Settings"
  label "Interface" "$iface"
  label "Mode" "$mode"

  form "confirm"
    checkbox "apply_now" "Apply immediately"
    button "← Back" "/page/setup.mu"
    button "Finish ✓" "/page/setup_done.mu?interface=$iface&mode=$mode"
```

### 6.4 Chat Room (community pattern)

```
page "Chat" 64
  cache 0

  state "chat" "/var/nomadnet/chatlog.json"
  source timestamp : python "datetime.now().strftime('%H:%M')"

  heading 1 "Node Chat"

  # Display last 15 messages
  for msg in $chat.messages[-15:]
    text "@bold{@color{$msg.color}{$msg.nick}}  @color{888}{$msg.time}"
    text "  $msg.text"

  divider light

  form "send"
    field "nick" 12 "Nickname"
    field "text" 40 "Type message..."
    button "Send" "/page/chat.mu"

  on_submit "send"
    if $nick && $text
      source color : python "format(hash('$nick')%4095,'03x')"
      append chat.messages { nick: $nick, text: $text, time: $timestamp, color: $color }

  text "@center{@italic{@color{888}{Ctrl+R to refresh · $chat.messages.length messages}}}"
```

### 6.5 Interactive Data Explorer

```
page "Peer Explorer" 64
  cache 0

  source peers_json : shell "rnstatus --json 2>/dev/null"
  source selected : param "hash"

  heading 1 "Peer Explorer"

  table "Peers"
    columns "Name" 24 | "Hops" 6 | "RTT" 8 | "Status" 10
    for peer in $peers_json.peers
      row "$peer.name" | "$peer.hops" | "$peer.rtt" | "$peer.status"

  if $selected
    divider heavy
    source detail : shell "rnstatus --peer $selected --json 2>/dev/null"

    box double "Peer Detail: $detail.name"
      label "Hash"      "$detail.hash"
      label "Address"   "$detail.address"
      label "Hops"      "$detail.hops"
      label "Latency"   "$detail.rtt"
      label "Last Seen" "$detail.last_seen"
      label "Transport" "$detail.transport"

      sparkline "Latency (24h)" $detail.latency_history 40

      row 2
        link "Ping" "/page/action.mu?cmd=ping&hash=$detail.hash"
        link "Trace" "/page/action.mu?cmd=trace&hash=$detail.hash"
        link "Browse" "$detail.hash:/page/index.mu"
```

---

## 7. Security Considerations

Dynamic pages execute code on the node server. µFrame enforces:

- **Shell command sanitization**: all `$variable` values interpolated
  into shell commands are escaped with `shlex.quote()` to prevent
  injection
- **State file isolation**: state files are restricted to a
  configurable directory (default: `/var/nomadnet/uframe/`)
- **Execution timeout**: all shell sources have a default 5-second
  timeout, configurable per source
- **No network egress by default**: source commands run in the
  node's local context — they can read local system data but
  µFrame does not add network capabilities beyond what the
  scripts themselves invoke
- **Input validation**: field values are length-limited and
  sanitized before use in sources or state operations

```
# In the DSL, explicit sanitization:
source result : shell "search.py" --arg $query --sanitize
  timeout 10
  max_length 256
```

---

## 8. Architecture Summary

```
                         ┌──────────────────────────────┐
                         │         .uf Source            │
                         │  (layout + sources + forms    │
                         │   + conditionals + state)     │
                         └──────────────┬───────────────┘
                                        │
                                   ┌────▼────┐
                                   │  Parse  │
                                   └────┬────┘
                                        │
                                   ┌────▼────┐
                                   │   IR    │
                                   │  Tree   │
                                   └──┬───┬──┘
                                      │   │
                       ┌──────────────┘   └────────────────┐
                       │                                   │
                ┌──────▼──────┐                    ┌───────▼───────┐
                │ render mode │                    │ compile mode  │
                │ (immediate) │                    │ (codegen)     │
                └──┬───────┬──┘                    └───────┬───────┘
                   │       │                               │
            ┌──────▼┐  ┌───▼─────┐                ┌───────▼────────┐
            │ ASCII │  │ Static  │                │ Executable .mu │
            │ stdout│  │ .mu file│                │ Python script  │
            └───────┘  └─────────┘                │ with embedded  │
                                                  │ runtime +      │
                                                  │ data sources + │
                                                  │ form handling  │
                                                  └───────┬────────┘
                                                          │
                                                     chmod +x
                                                     deploy to
                                                          │
                                                ┌─────────▼─────────┐
                                                │   NomadNet Node   │
                                                │  ~/.nomadnetwork/ │
                                                │  storage/pages/   │
                                                │                   │
                                                │  Client request → │
                                                │  Execute script → │
                                                │  Stdout = Micron  │
                                                │  with live data,  │
                                                │  rich ASCII art,  │
                                                │  color + forms    │
                                                └───────────────────┘
```

The dynamic model turns µFrame from a static template engine into a
**full application framework for NomadNet** — where the same DSL that
defines the visual layout also defines the data flow, user interaction,
and server-side logic. The ASCII art isn't decoration — it's the UI
of a live, interactive, decentralized application running over
encrypted mesh networks.
