Data Flow: PTY to Screen
How your terminal output travels through MonoTerm’s rendering pipeline.Complete Data Flow
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ COMPLETE DATA FLOW │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────┐ │
│ │ Shell/App │ Output: "ls" --> colored file list │
│ │ (zsh, vim) │ │
│ └─────┬──────┘ │
│ │ │
│ │ PTY (pseudo-terminal) │
│ ▼ │
│ ┌────────────┐ │
│ │ PTY Daemon │ Rust sidecar binary │
│ │ │ Reads PTY, writes to Unix Socket │
│ └─────┬──────┘ │
│ │ │
│ │ Unix Socket (per session) │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ TAURI BACKEND (Rust) │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ SessionActor│────────→│ GridWorker │ │ │
│ │ │ (owns state)│ spawn │ (Tokio task)│ │ │
│ │ └─────────────┘ └──────┬──────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────┐ │ │
│ │ │ Alacritty │ │ │
│ │ │ VTE Parser │ │ │
│ │ └──────┬──────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────┐ │ │
│ │ │ Smart Diff │ │ │
│ │ │ (DiffHint) │ │ │
│ │ └──────┬──────┘ │ │
│ │ │ │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Tauri Event (JSON) │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ FRONTEND (TypeScript) │ │
│ │ │ │
│ │ ┌───────────────────┐ │ │
│ │ │ Direct Injection │ Cells injected into xterm.js │ │
│ │ └────────┬──────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌───────────────────┐ │ │
│ │ │ WebGL Renderer │ GPU-accelerated display │ │
│ │ └───────────────────┘ │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────┘
Step-by-Step Breakdown
Step 1: Your Program Runs
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ STEP 1: Program generates output │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ $ npm install │ │
│ │ │ │
│ │ npm wants to show: │ │
│ │ │ │
│ │ "Installing packages..." │ │
│ │ "[########............] 45%" │ │
│ │ │ │
│ │ This gets converted to ESCAPE CODES: │ │
│ │ │ │
│ │ ESC[32m = "start green color" │ │
│ │ ESC[0m = "reset color" │ │
│ │ ESC[2K = "clear line" │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ │
│ These codes control colors, cursor position, and more. │
│ │
└───────────────────────────────────────────────────────────────────────┘
Step 2: Data Travels Through PTY
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ STEP 2: Data travels through PTY (pseudo-terminal) │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ │
│ npm MonoTerm │
│ ┌─────┐ ┌─────┐ │
│ │ │ │ │ │
│ │ N │════════════════════════════════│ M │ │
│ │ P │ PTY Pipe │ O │ │
│ │ M │ (like a water hose) │ N │ │
│ │ │ │ O │ │
│ └─────┘ └─────┘ │
│ │
│ │
│ The pipe can SPLIT data randomly: │
│ │
│ │
│ npm sends: "ESC[32mHelloESC[0m" │
│ │ │
│ ┌──────┴──────┐ │
│ │ THE PIPE │ │
│ └──────┬──────┘ │
│ │ │
│ MonoTerm gets: "ESC[32mHel" then "loESC[0m" │
│ │ │ │
│ chunk 1 chunk 2 │
│ │
│ │
│ This splitting is RANDOM and unpredictable! │
│ │
└───────────────────────────────────────────────────────────────────────┘
Step 3: Rust Backend Processing
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ STEP 3: SessionActor receives and routes data │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ │
│ ┌───────────────────┐ │
│ │ SessionActor │ Single owner - no locks needed! │
│ │ │ │
│ │ Owns all state │ Actor Model pattern │
│ └────────┬──────────┘ │
│ │ │
│ │ MPSC channel (lock-free message passing) │
│ ▼ │
│ ┌───────────────────┐ │
│ │ GridWorker │ Tokio async task │
│ │ │ │
│ │ loop { │ │
│ │ receive data │ │
│ │ parse VTE │ │
│ │ compute diff │ │
│ │ emit update │ │
│ │ } │ │
│ └───────────────────┘ │
│ │
│ │
│ Why Actor Model? │
│ │
│ * No mutex contention │
│ * No deadlocks possible │
│ * Guaranteed ordering │
│ * Easy to reason about │
│ │
└───────────────────────────────────────────────────────────────────────┘
Step 4: VTE Parser (Alacritty)
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ STEP 4: Alacritty VTE Parser interprets escape sequences │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ │
│ Raw bytes: [0x1b, 0x5b, 0x33, 0x32, 0x6d, 0x48, 0x65, 0x6c, ...] │
│ │
│ │ │
│ ▼ │
│ ┌───────────────────┐ │
│ │ Alacritty VTE │ │
│ │ │ │
│ │ Parse escape │ │
│ │ sequences │ │
│ └────────┬──────────┘ │
│ │ │
│ ▼ │
│ │
│ Grid cells: │
│ ┌───────┬───────┬───────┬───────┬───────┐ │
│ │ H │ e │ l │ l │ o │ │
│ │ green │ green │ green │ green │ green │ │
│ └───────┴───────┴───────┴───────┴───────┘ │
│ │
│ │
│ What Alacritty provides: │
│ │
│ * Character content │
│ * Foreground color │
│ * Background color │
│ * Text attributes (bold, underline, etc.) │
│ * Cursor position │
│ │
└───────────────────────────────────────────────────────────────────────┘
Step 5: Smart Diff Calculation
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ STEP 5: DiffHint determines what changed │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ │
│ Previous State New State │
│ │
│ Line 0: "$ cd project" Line 0: "$ cd project" SAME │
│ Line 1: "$ npm install" Line 1: "$ npm install" SAME │
│ Line 2: "Loading..." Line 2: "Done!" CHANGED │
│ Line 3: "" Line 3: "" SAME │
│ │
│ │
│ Result: Partial { dirty_rows: [2] } │
│ │
│ │
│ ┌───────────────────┐ ┌───────────────────┐ ┌───────────────┐ │
│ │ Full │ │ Partial │ │ None │ │
│ │ │ │ │ │ │ │
│ │ ############ │ │ ............ │ │ .......... │ │
│ │ ############ │ │ ..########.. │ │ .......... │ │
│ │ ############ │ │ ............ │ │ ...._.... │ │
│ │ All rows │ │ Dirty rows │ │ Cursor only │ │
│ └───────────────────┘ └───────────────────┘ └───────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────┘
Step 6: Frontend Injection
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ STEP 6: Direct Buffer Injection into xterm.js │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ │
│ Tauri Event arrives with GridUpdate │
│ │
│ │ │
│ ▼ │
│ ┌───────────────────┐ │
│ │ Buffer Injector │ │
│ │ │ │
│ │ DiffHint check │ │
│ └────────┬──────────┘ │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ Full │ │ Partial│ │ None │ │
│ │ │ │ │ │ │ │
│ │ New │ │ Update │ │ Cursor │ │
│ │ buffer │ │ rows │ │ only │ │
│ └────────┘ └────────┘ └────────┘ │
│ │
│ │
│ xterm.js buffer gets updated directly: │
│ │
│ buffer.lines[i]._data = [content, fg, bg, ...] │
│ │
│ │ │
│ ▼ │
│ ┌───────────────────┐ │
│ │ WebGL Renderer │ │
│ │ │ │
│ │ GPU-accelerated │ │
│ │ display │ │
│ └───────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────┘
ACK Handshake (Flow Control)
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ ACK HANDSHAKE: Prevents overwhelming the frontend │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ │
│ RUST BACKEND FRONTEND │
│ ┌────────────┐ ┌────────────┐ │
│ │ │ Frame 1 │ │ │
│ │ │──────────────────────→│ Display │ │
│ │ │ │ │ │
│ │ WAITING │←──────────────────────│ "OK!" │ │
│ │ ... │ ACK received │ │ │
│ │ │ │ │ │
│ │ │ Frame 2 │ │ │
│ │ │──────────────────────→│ Display │ │
│ │ │ │ │ │
│ │ │←──────────────────────│ "OK!" │ │
│ │ │ ACK │ │ │
│ └────────────┘ └────────────┘ │
│ │
│ │
│ WHY HANDSHAKE? │
│ │
│ Without ACK: │
│ │
│ Send ──→ Send ──→ Send ──→ Send ──→ Send │
│ │ │
│ Frontend overwhelmed! │
│ │
│ With ACK: │
│ │
│ Send ──→ Wait ──→ ACK ──→ Send ──→ Wait ──→ ACK │
│ │ │
│ Smooth processing │
│ │
└───────────────────────────────────────────────────────────────────────┘
Timing Diagram
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ TIME ────────────────────────────────────────────────────────────→ │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ PTY: ─────[data1]────────[data2]─────[data3]───────────────── │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ VTE: ──────[*]────────────[*]──────────[*]─────────────────── │
│ │ │
│ │ complete frame │
│ ▼ │
│ Diff: ─────────────────────────────────────────[DiffHint]───── │
│ │ │
│ │ emit │
│ ▼ │
│ Inject: ─────────────────────────────────────────[*]──────────── │
│ │ │
│ │ ACK │
│ ▼ │
│ Render: ──────────────────────────────────────────────[*]─────── │
│ │
│ <──────────────────── ~50ms ───────────────────────────→ │
│ (tick interval) │
│ │
└───────────────────────────────────────────────────────────────────────┘
Summary
Copy
┌───────────────────────────────────────────────────────────────────────┐
│ │
│ THE COMPLETE JOURNEY │
│ │
│ │
│ 1. BORN 2. TRAVEL 3. PARSE │
│ │
│ ┌─────────┐ ═══════════ ┌─────────┐ │
│ │ vim │ │ PTY │ │ Alacritty│ │
│ │ says │══════════════→│ Pipe │══════════════→│ VTE │ │
│ │ "Hello" │ │ (chunks) │ │ Parser │ │
│ └─────────┘ ═══════════ └─────────┘ │
│ │
│ │
│ 4. DIFF 5. INJECT 6. DISPLAY │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Smart │ │ Direct │ │ WebGL │ │
│ │ Diff │══════════════→│ Buffer │══════════════→│ Render │ │
│ │ (Rust) │ │ Inject │ │ (GPU) │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ │
│ 7. ACKNOWLEDGE │
│ │
│ ┌─────────────┐ │
│ │ Frontend │ │
│ │ says │ │
│ │ "Got it!" │ │
│ │ │ │
│ │ READY FOR │ │
│ │ NEXT │ │
│ └─────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────┘
Architecture
SessionActor (Rust) owns all state with MPSC channels for lock-free communication. Alacritty VTE parses escape sequences. WebGL renders to screen.