Skip to main content

Dual-Loop Architecture

Monolex uses two distinct loops for terminal rendering: a Data Loop that ingests PTY output, and a Render Loop that pushes updates to the frontend.

The Big Picture

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃                                                                                            ┃
┃                           MONOLEX DUAL-LOOP ARCHITECTURE                                   ┃
┃                                                                                            ┃
┃  ┌──────────────────────────────────────────────────────────────────────────────────────┐  ┃
┃  │                                                                                      │  ┃
┃  │     USER                                                                             │  ┃
┃  │      │                                                                               │  ┃
┃  │      │ keyboard input                                                                │  ┃
┃  │      ▼                                                                               │  ┃
┃  │  ┌───────┐         ┌─────────────────────────────────────────────────────────────┐  │  ┃
┃  │  │ SHELL │ ◄──────►│                    PTY DAEMON                               │  │  ┃
┃  │  │ bash  │  stdin  │                    (pty-daemon-rust)                        │  │  ┃
┃  │  │ zsh   │  stdout │                                                             │  │  ┃
┃  │  └───────┘         └──────────────────────────┬──────────────────────────────────┘  │  ┃
┃  │                                               │                                      │  ┃
┃  │                                               │ Unix Socket                          │  ┃
┃  │                                               │ (raw bytes)                          │  ┃
┃  │                                               ▼                                      │  ┃
┃  │  ╔════════════════════════════════════════════════════════════════════════════════╗ │  ┃
┃  │  ║                                                                                ║ │  ┃
┃  │  ║                              RUST BACKEND                                      ║ │  ┃
┃  │  ║                                                                                ║ │  ┃
┃  │  ║  ┌─────────────────────────────────────────────────────────────────────────┐  ║ │  ┃
┃  │  ║  │                                                                         │  ║ │  ┃
┃  │  ║  │   ╭─────────────────────────────────────────────────────────────────╮   │  ║ │  ┃
┃  │  ║  │   │                                                                 │   │  ║ │  ┃
┃  │  ║  │   │             ██████╗  █████╗ ████████╗ █████╗                    │   │  ║ │  ┃
┃  │  ║  │   │             ██╔══██╗██╔══██╗╚══██╔══╝██╔══██╗                   │   │  ║ │  ┃
┃  │  ║  │   │             ██║  ██║███████║   ██║   ███████║                   │   │  ║ │  ┃
┃  │  ║  │   │             ██║  ██║██╔══██║   ██║   ██╔══██║                   │   │  ║ │  ┃
┃  │  ║  │   │             ██████╔╝██║  ██║   ██║   ██║  ██║                   │   │  ║ │  ┃
┃  │  ║  │   │             ╚═════╝ ╚═╝  ╚═╝   ╚═╝   ╚═╝  ╚═╝                   │   │  ║ │  ┃
┃  │  ║  │   │                      L O O P  (Data Ingestion)                  │   │  ║ │  ┃
┃  │  ║  │   │                                                                 │   │  ║ │  ┃
┃  │  ║  │   │   Trigger: EVENT (PTY data arrival)                             │   │  ║ │  ┃
┃  │  ║  │   │   Control: NONE (always runs)                                   │   │  ║ │  ┃
┃  │  ║  │   │                                                                 │   │  ║ │  ┃
┃  │  ║  │   │   ┌─────────────┐    ┌─────────────┐    ┌─────────────────────┐ │   │  ║ │  ┃
┃  │  ║  │   │   │             │    │             │    │                     │ │   │  ║ │  ┃
┃  │  ║  │   │   │  socket     │───►│   feed()    │───►│   VTE Parser        │ │   │  ║ │  ┃
┃  │  ║  │   │   │  .recv()    │    │   buffer    │    │   (Alacritty)       │ │   │  ║ │  ┃
┃  │  ║  │   │   │             │    │   BSU/ESU   │    │                     │ │   │  ║ │  ┃
┃  │  ║  │   │   └─────────────┘    └─────────────┘    └──────────┬──────────┘ │   │  ║ │  ┃
┃  │  ║  │   │                                                    │            │   │  ║ │  ┃
┃  │  ║  │   │                                                    ▼            │   │  ║ │  ┃
┃  │  ║  │   │                                         ┌─────────────────────┐ │   │  ║ │  ┃
┃  │  ║  │   │                                         │                     │ │   │  ║ │  ┃
┃  │  ║  │   │                                         │   Grid<Cell>        │ │   │  ║ │  ┃
┃  │  ║  │   │                                         │   (Ring Buffer)     │ │   │  ║ │  ┃
┃  │  ║  │   │                                         │                     │ │   │  ║ │  ┃
┃  │  ║  │   │                                         │   pending_data=true │ │   │  ║ │  ┃
┃  │  ║  │   │                                         │         │           │ │   │  ║ │  ┃
┃  │  ║  │   │                                         └─────────┼───────────┘ │   │  ║ │  ┃
┃  │  ║  │   │                                                   │             │   │  ║ │  ┃
┃  │  ║  │   ╰───────────────────────────────────────────────────┼─────────────╯   │  ║ │  ┃
┃  │  ║  │                                                       │                 │  ║ │  ┃
┃  │  ║  │                                                       │                 │  ║ │  ┃
┃  │  ║  │                                                       ▼                 │  ║ │  ┃
┃  │  ║  │          ┌────────────────────────────────────────────────────────┐     │  ║ │  ┃
┃  │  ║  │          │                    SHARED STATE                        │     │  ║ │  ┃
┃  │  ║  │          │                                                        │     │  ║ │  ┃
┃  │  ║  │          │   • Grid<Cell>      - Terminal content                 │     │  ║ │  ┃
┃  │  ║  │          │   • pending_data    - "New data available" flag        │     │  ║ │  ┃
┃  │  ║  │          │   • syncing         - BSU/ESU sync flag                │     │  ║ │  ┃
┃  │  ║  │          │   • waiting_ack     - ACK pending flag                 │     │  ║ │  ┃
┃  │  ║  │          │   • line_hashes[]   - For diff calculation             │     │  ║ │  ┃
┃  │  ║  │          │                                                        │     │  ║ │  ┃
┃  │  ║  │          └────────────────────────────────────────────────────────┘     │  ║ │  ┃
┃  │  ║  │                                                       │                 │  ║ │  ┃
┃  │  ║  │                                                       │                 │  ║ │  ┃
┃  │  ║  │   ╭───────────────────────────────────────────────────┼─────────────╮   │  ║ │  ┃
┃  │  ║  │   │                                                   │             │   │  ║ │  ┃
┃  │  ║  │   │   ██████╗ ███████╗███╗   ██╗██████╗ ███████╗██████╗│             │   │  ║ │  ┃
┃  │  ║  │   │   ██╔══██╗██╔════╝████╗  ██║██╔══██╗██╔════╝██╔══██╗            │   │  ║ │  ┃
┃  │  ║  │   │   ██████╔╝█████╗  ██╔██╗ ██║██║  ██║█████╗  ██████╔╝            │   │  ║ │  ┃
┃  │  ║  │   │   ██╔══██╗██╔══╝  ██║╚██╗██║██║  ██║██╔══╝  ██╔══██╗            │   │  ║ │  ┃
┃  │  ║  │   │   ██║  ██║███████╗██║ ╚████║██████╔╝███████╗██║  ██║            │   │  ║ │  ┃
┃  │  ║  │   │   ╚═╝  ╚═╝╚══════╝╚═╝  ╚═══╝╚═════╝ ╚══════╝╚═╝  ╚═╝            │   │  ║ │  ┃
┃  │  ║  │   │         L O O P  (Render Push)                    ▼             │   │  ║ │  ┃
┃  │  ║  │   │                                                                 │   │  ║ │  ┃
┃  │  ║  │   │   Trigger: TIMER (tick_interval)                                │   │  ║ │  ┃
┃  │  ║  │   │   Control: Hz Detection (loop frequency)                        │   │  ║ │  ┃
┃  │  ║  │   │            ACK Gating (emit permission)                         │   │  ║ │  ┃
┃  │  ║  │   │                                                                 │   │  ║ │  ┃
┃  │  ║  │   │   ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐  │   │  ║ │  ┃
┃  │  ║  │   │     Hz Detection Gate                                        │  │   │  ║ │  ┃
┃  │  ║  │   │   │ tick_interval = frames(3)                                   │   │  ║ │  ┃
┃  │  ║  │   │     60Hz: 50ms │ 30Hz: 100ms                                 │  │   │  ║ │  ┃
┃  │  ║  │   │   └ ─ ─ ─ ─ ─ ─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘  │   │  ║ │  ┃
┃  │  ║  │   │                │                                                │   │  ║ │  ┃
┃  │  ║  │   │                ▼                                                │   │  ║ │  ┃
┃  │  ║  │   │   ┌─────────────────────┐                                       │   │  ║ │  ┃
┃  │  ║  │   │   │                     │                                       │   │  ║ │  ┃
┃  │  ║  │   │   │   tick()            │  Check timeouts                       │   │  ║ │  ┃
┃  │  ║  │   │   │   - sync_deadline   │  - BSU timeout                        │   │  ║ │  ┃
┃  │  ║  │   │   │   - ack_deadline    │  - ACK timeout                        │   │  ║ │  ┃
┃  │  ║  │   │   │                     │                                       │   │  ║ │  ┃
┃  │  ║  │   │   └──────────┬──────────┘                                       │   │  ║ │  ┃
┃  │  ║  │   │              │                                                  │   │  ║ │  ┃
┃  │  ║  │   │              ▼                                                  │   │  ║ │  ┃
┃  │  ║  │   │   ┌─────────────────────┐                                       │   │  ║ │  ┃
┃  │  ║  │   │   │                     │                                       │   │  ║ │  ┃
┃  │  ║  │   │   │   pull()            │  Build GridUpdate                     │   │  ║ │  ┃
┃  │  ║  │   │   │   - read Grid       │  - Read cells                         │   │  ║ │  ┃
┃  │  ║  │   │   │   - compute hash    │  - Calculate diff                     │   │  ║ │  ┃
┃  │  ║  │   │   │   - build update    │  - Encode data                        │   │  ║ │  ┃
┃  │  ║  │   │   │                     │                                       │   │  ║ │  ┃
┃  │  ║  │   │   │   ┌───────────────────────────────────────────┐             │   │  ║ │  ┃
┃  │  ║  │   │   │   │  ACK Gate                                 │             │   │  ║ │  ┃
┃  │  ║  │   │   │   │  waiting_ack? ──► true  → return None     │             │   │  ║ │  ┃
┃  │  ║  │   │   │   │                   false → return Some()   │             │   │  ║ │  ┃
┃  │  ║  │   │   │   └───────────────────────────────────────────┘             │   │  ║ │  ┃
┃  │  ║  │   │   │                     │                                       │   │  ║ │  ┃
┃  │  ║  │   │   └──────────┬──────────┘                                       │   │  ║ │  ┃
┃  │  ║  │   │              │                                                  │   │  ║ │  ┃
┃  │  ║  │   │              │ Some(GridUpdate)                                 │   │  ║ │  ┃
┃  │  ║  │   │              ▼                                                  │   │  ║ │  ┃
┃  │  ║  │   │   ┌─────────────────────┐                                       │   │  ║ │  ┃
┃  │  ║  │   │   │                     │                                       │   │  ║ │  ┃
┃  │  ║  │   │   │   emit()            │  Send to frontend                     │   │  ║ │  ┃
┃  │  ║  │   │   │   Binary Channel    │  waiting_ack = true                   │   │  ║ │  ┃
┃  │  ║  │   │   │                     │                                       │   │  ║ │  ┃
┃  │  ║  │   │   └──────────┬──────────┘                                       │   │  ║ │  ┃
┃  │  ║  │   │              │                                                  │   │  ║ │  ┃
┃  │  ║  │   ╰──────────────┼──────────────────────────────────────────────────╯   │  ║ │  ┃
┃  │  ║  │                  │                                                      │  ║ │  ┃
┃  │  ║  └──────────────────┼──────────────────────────────────────────────────────┘  ║ │  ┃
┃  │  ║                     │                                                         ║ │  ┃
┃  │  ╚═════════════════════╪═════════════════════════════════════════════════════════╝ │  ┃
┃  │                        │                                                           │  ┃
┃  │                        │ Tauri IPC (Binary)                                        │  ┃
┃  │                        ▼                                                           │  ┃
┃  │  ╔════════════════════════════════════════════════════════════════════════════════╗│  ┃
┃  │  ║                                                                                ║│  ┃
┃  │  ║                              FRONTEND (WebKit)                                 ║│  ┃
┃  │  ║                                                                                ║│  ┃
┃  │  ║   ┌────────────────────────────────────────────────────────────────────────┐  ║│  ┃
┃  │  ║   │                                                                        │  ║│  ┃
┃  │  ║   │   onGridUpdate()                                                       │  ║│  ┃
┃  │  ║   │        │                                                               │  ║│  ┃
┃  │  ║   │        ▼                                                               │  ║│  ┃
┃  │  ║   │   decode() ───► inject() ───► xterm.js Buffer ───► WebGL Render       │  ║│  ┃
┃  │  ║   │                                                          │             │  ║│  ┃
┃  │  ║   │                                                          │             │  ║│  ┃
┃  │  ║   │                                                          ▼             │  ║│  ┃
┃  │  ║   │                                              requestAnimationFrame     │  ║│  ┃
┃  │  ║   │                                              (measured by Hz Detection)│  ║│  ┃
┃  │  ║   │                                                          │             │  ║│  ┃
┃  │  ║   │                                                          ▼             │  ║│  ┃
┃  │  ║   │   invoke('ack') ◄────────────────────────────── Render Complete        │  ║│  ┃
┃  │  ║   │        │                                                               │  ║│  ┃
┃  │  ║   │        │                                                               │  ║│  ┃
┃  │  ║   └────────┼───────────────────────────────────────────────────────────────┘  ║│  ┃
┃  │  ║            │                                                                   ║│  ┃
┃  │  ╚════════════╪═══════════════════════════════════════════════════════════════════╝│  ┃
┃  │               │                                                                    │  ┃
┃  │               │ ACK                                                                │  ┃
┃  │               │                                                                    │  ┃
┃  │               ▼                                                                    │  ┃
┃  │        waiting_ack = false                                                         │  ┃
┃  │        (unblock next emit)                                                         │  ┃
┃  │                                                                                    │  ┃
┃  │               │                                                                    │  ┃
┃  │               │ Visual output                                                      │  ┃
┃  │               ▼                                                                    │  ┃
┃  │                                                                                    │  ┃
┃  │           👁️ USER                                                                  │  ┃
┃  │                                                                                    │  ┃
┃  └────────────────────────────────────────────────────────────────────────────────────┘  ┃
┃                                                                                          ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

Simplified View

┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                                                                                         │
│   DATA LOOP (Event-Driven)               RENDER LOOP (Timer-Driven)                     │
│   ════════════════════════               ═══════════════════════════                    │
│                                                                                         │
│   ╭───────────────────────╮              ╭───────────────────────╮                      │
│   │                       │              │                       │                      │
│   │   PTY Data Arrives    │              │   tick_interval fires │◄── Hz Detection     │
│   │         │             │              │         │             │    controls this    │
│   │         ▼             │              │         ▼             │                      │
│   │   ┌───────────┐       │              │   ┌───────────┐       │                      │
│   │   │  feed()   │       │              │   │  tick()   │       │                      │
│   │   └─────┬─────┘       │              │   └─────┬─────┘       │                      │
│   │         │             │              │         │             │                      │
│   │         ▼             │              │         ▼             │                      │
│   │   ┌───────────┐       │              │   ┌───────────┐       │                      │
│   │   │   VTE     │       │              │   │  pull()   │       │                      │
│   │   │  Parser   │       │              │   │           │◄──────┼── ACK controls this │
│   │   └─────┬─────┘       │              │   └─────┬─────┘       │                      │
│   │         │             │              │         │             │                      │
│   │         ▼             │              │         ▼             │                      │
│   │   ┌───────────┐       │              │   ┌───────────┐       │                      │
│   │   │   Grid    │───────┼──────────────┼──►│  emit()   │       │                      │
│   │   │  Update   │       │  pending     │   └─────┬─────┘       │                      │
│   │   └───────────┘       │  _data=true  │         │             │                      │
│   │                       │              │         │             │                      │
│   ╰───────────────────────╯              │         ▼             │                      │
│                                          │   ┌───────────┐       │                      │
│                                          │   │  WebKit   │       │                      │
│                                          │   │  inject   │       │                      │
│                                          │   └─────┬─────┘       │                      │
│                                          │         │             │                      │
│                                          │         ▼             │                      │
│                                          │   ┌───────────┐       │                      │
│                                          │   │   ACK     │───────┼──► waiting_ack=false│
│                                          │   └───────────┘       │                      │
│                                          │                       │                      │
│                                          ╰───────────────────────╯                      │
│                                                                                         │
│   No Control                             Two Controls:                                  │
│   (Always runs)                          • Hz = Loop frequency                          │
│                                          • ACK = Emit permission                        │
│                                                                                         │
└─────────────────────────────────────────────────────────────────────────────────────────┘

Loop Interaction via Shared State

┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                                                                                         │
│                              SHARED STATE (AtomicState)                                 │
│                                                                                         │
│   ┌─────────────────────────────────────────────────────────────────────────────────┐  │
│   │                                                                                 │  │
│   │    ┌──────────────┐   ┌──────────────┐   ┌──────────────┐   ┌──────────────┐   │  │
│   │    │ Grid<Cell>   │   │ pending_data │   │   syncing    │   │ waiting_ack  │   │  │
│   │    │              │   │              │   │              │   │              │   │  │
│   │    │  (content)   │   │   (flag)     │   │   (flag)     │   │   (flag)     │   │  │
│   │    └──────┬───────┘   └──────┬───────┘   └──────┬───────┘   └──────┬───────┘   │  │
│   │           │                  │                  │                  │           │  │
│   └───────────┼──────────────────┼──────────────────┼──────────────────┼───────────┘  │
│               │                  │                  │                  │              │
│               │                  │                  │                  │              │
│   ╔═══════════╪══════════════════╪══════════════════╪══════════════════╪═══════════╗  │
│   ║           │                  │                  │                  │           ║  │
│   ║  DATA     │ WRITE            │ WRITE            │ WRITE            │           ║  │
│   ║  LOOP     ▼                  ▼                  ▼                  │           ║  │
│   ║                                                                    │           ║  │
│   ║  feed()  ─────► Grid ────► pending_data=true                       │           ║  │
│   ║          ─────────────────► syncing=true (if BSU)                  │           ║  │
│   ║                                                                    │           ║  │
│   ╚════════════════════════════════════════════════════════════════════╪═══════════╝  │
│                                                                        │              │
│                                                                        │              │
│   ╔════════════════════════════════════════════════════════════════════╪═══════════╗  │
│   ║                                                                    │           ║  │
│   ║  RENDER   │ READ             │ READ             │ READ             │ READ      ║  │
│   ║  LOOP     ▼                  ▼                  ▼                  ▼ WRITE     ║  │
│   ║                                                                                ║  │
│   ║  pull()  ◄───── Grid ◄───── pending_data? ◄─── syncing? ◄─── waiting_ack?     ║  │
│   ║                                                                    │           ║  │
│   ║          ───────────────────────────────────────────────► waiting_ack=true    ║  │
│   ║                                                            (after emit)       ║  │
│   ║                                                                                ║  │
│   ║  ack()   ─────────────────────────────────────────────────► waiting_ack=false ║  │
│   ║                                                            (from frontend)    ║  │
│   ║                                                                                ║  │
│   ╚════════════════════════════════════════════════════════════════════════════════╝  │
│                                                                                         │
└─────────────────────────────────────────────────────────────────────────────────────────┘

Timing Comparison

┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                                                                                         │
│   TIME (ms)    0    10   20   30   40   50   60   70   80   90   100  110  120         │
│                │    │    │    │    │    │    │    │    │    │    │    │    │           │
│                ▼    ▼    ▼    ▼    ▼    ▼    ▼    ▼    ▼    ▼    ▼    ▼    ▼           │
│   ════════════════════════════════════════════════════════════════════════════════     │
│                                                                                         │
│   DATA LOOP    ●    ●              ●●●●●●●●●●         ●    ●         ●●●●              │
│   (PTY data)   │    │              │         │        │    │         │                 │
│                │    │              │  (cat   │        │    │         │ (ls)            │
│                │    │              │   file) │        │    │         │                 │
│                ▼    ▼              ▼         ▼        ▼    ▼         ▼                 │
│                                                                                         │
│   Grid         █    █              █████████████      █    █         ████              │
│   Updates      │    │              │         │        │    │         │                 │
│                │    │              │         │        │    │         │                 │
│   ════════════════════════════════════════════════════════════════════════════════     │
│                                                                                         │
│   RENDER LOOP  ┃         ┃         ┃         ┃         ┃         ┃                     │
│   @ 60Hz       ┃         ┃         ┃         ┃         ┃         ┃                     │
│   (50ms tick)  ┃         ┃         ┃         ┃         ┃         ┃                     │
│                ▼         ▼         ▼         ▼         ▼         ▼                     │
│                                                                                         │
│   tick         T         T         T         T         T         T                     │
│   pull         P→emit    P→emit    P→emit    P→emit    P→emit    P→emit               │
│                                                                                         │
│   ════════════════════════════════════════════════════════════════════════════════     │
│                                                                                         │
│   RENDER LOOP           ┃                   ┃                   ┃                      │
│   @ 30Hz                ┃                   ┃                   ┃                      │
│   (100ms tick)          ┃                   ┃                   ┃                      │
│                         ▼                   ▼                   ▼                      │
│                                                                                         │
│   tick                  T                   T                   T                      │
│   pull                  P→emit              P→emit              P→emit                 │
│                                                                                         │
│   ════════════════════════════════════════════════════════════════════════════════     │
│                                                                                         │
│   Note: Data loop timing is IRREGULAR (depends on PTY data arrival)                    │
│         Render loop timing is REGULAR (depends on tick_interval)                       │
│                                                                                         │
│   Data loop may fire 0 times, 1 time, or 100 times between render loop ticks          │
│   Render loop always fires at fixed intervals (controlled by Hz)                       │
│                                                                                         │
└─────────────────────────────────────────────────────────────────────────────────────────┘

Summary

AspectData LoopRender Loop
TriggerEvent (PTY data)Timer (tick_interval)
FrequencyIrregular (0~N per tick)Regular (Hz-based)
ControlNoneHz + ACK
PurposeData ingestionRender push
What it doesParse → StoreRead → Build → Emit
CPU costPer-data (can’t reduce)Per-tick (Hz reduces)
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                                                                                         │
│   Data Loop:    "Process data whenever it arrives" (passive, reactive)                  │
│                                                                                         │
│   Render Loop:  "Periodically check state and send" (active, timer-based)               │
│                                                                                         │
│   ─────────────────────────────────────────────────────────────────────────────────     │
│                                                                                         │
│   Hz Detection:  Controls the Render Loop "frequency"                                   │
│   ACK Gating:    Controls the Render Loop "emit permission"                             │
│                                                                                         │
│   Data Loop cannot be controlled (data must be processed when it arrives)               │
│                                                                                         │
└─────────────────────────────────────────────────────────────────────────────────────────┘