Documentation Index
Fetch the complete documentation index at: https://docs.monolex.ai/llms.txt
Use this file to discover all available pages before exploring further.
Design Patterns in MonoTerm
This document explains the key design patterns used in MonoTerm’s architecture. These patterns are language-independent design solutions, not Rust-specific features.┌─────────────────────────────────────────────────────────────────┐
│ PATTERN ORIGINS │
├─────────────────────────────────────────────────────────────────┤
│ │
│ SessionActor Pattern ACK Flow Control │
│ ────────────────── ──────────────── │
│ │
│ Origin: Erlang (1986) Origin: TCP/IP (1974) │
│ Level: Concurrency Pattern Level: Network Protocol │
│ │
│ Actor Model (1973) TCP ACK (1974) │
│ │ │ │
│ ▼ ▼ │
│ Erlang/OTP Backpressure │
│ │ │ │
│ ▼ ▼ │
│ Akka (Scala), Orleans (C#) Flow Control │
│ │ │ │
│ ▼ ▼ │
│ Tokio MPSC (Rust) MonoTerm ACK │
│ │
│ Conclusion: Design pattern level, not system level │
│ │
└─────────────────────────────────────────────────────────────────┘
The Problem: Concurrent Access
Multiple parts of the system need to access session data simultaneously.┌─────────────────────────────────────────────────────────────────┐
│ THE CONCURRENT ACCESS PROBLEM │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Places that access "sessions" data: │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. User creates new tab Insert new session │ │
│ │ 2. PTY data arrives Write to session │ │
│ │ 3. User closes tab Remove session │ │
│ │ 4. Resize event Modify session │ │
│ │ 5. Tray menu lists tabs Read all sessions │ │
│ │ │ │
│ │ These can happen SIMULTANEOUSLY! │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Dangerous Situation: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Thread A: Processing PTY data... │ │
│ │ Get session "tab-1" │ │
│ │ Write data... While writing! │ │
│ │ │ │ │
│ │ Thread B: User closes tab! ▼ │ │
│ │ Remove "tab-1" Simultaneous delete! │ │
│ │ │ │
│ │ Result: Crash! │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Solution 1: Mutex (Traditional)
The traditional approach uses locks.┌─────────────────────────────────────────────────────────────────┐
│ MUTEX APPROACH │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Must acquire lock on every access │
│ │
│ Thread A Thread B Thread C │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ lock() lock() lock() │
│ │ │ WAIT │ WAIT │
│ working... │ │ │
│ │ │ │ │
│ unlock() │ │ │
│ working... │ │
│ │ │ │
│ unlock() │ │
│ working... │
│ │
│ Problems: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Lock Contention: Only one can work at a time │ │
│ │ Deadlock Risk: A waits for B, B waits for A = frozen │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Solution 2: Actor Pattern (MonoTerm)
MonoTerm uses the Actor pattern - no locks needed.┌─────────────────────────────────────────────────────────────────┐
│ ACTOR PATTERN │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Core Idea: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Don't touch data directly │ │
│ │ Send a message to the person in charge (Actor) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ │
│ Thread A ──┐ │
│ │ ┌─────────────┐ │
│ Thread B ──┼────▶│ MPSC │────▶ SessionActor │
│ │ │ Channel │ (single owner) │
│ Thread C ──┘ └─────────────┘ │ │
│ ▼ │
│ sessions │
│ (data) │
│ │
│ Rules: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Anyone can put messages in Channel (simultaneous OK) │ │
│ │ Only Actor accesses sessions (1 person = no conflicts) │ │
│ │ No locks needed! │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ═══════════════════════════════════════════════════════════ │
│ │
│ Analogy: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Mutex = Multiple people trying to open refrigerator │ │
│ │ Take turns one by one (Lock) │ │
│ │ Pushing causes accidents (Deadlock) │ │
│ │ │ │
│ │ Actor = One refrigerator manager │ │
│ │ Leave note "get me a coke please" │ │
│ │ Manager processes requests in order │ │
│ │ Conflicts are impossible │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
SessionActor Structure
The SessionActor owns all session state.┌─────────────────────────────────────────────────────────────────┐
│ SESSIONACTOR STRUCTURE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Message Types (what requests are possible): │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ CreateSession Create new tab │ │
│ │ CloseSession Close tab │ │
│ │ WriteData Write to PTY │ │
│ │ Resize Change terminal size │ │
│ │ GridAck ACK received from frontend │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Actor Owns: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ sessions Session data (Actor only!) │ │
│ │ grid_workers Per-session workers │ │
│ │ rx Mailbox (receives messages) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Actor Main Loop: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ loop { │ │
│ │ Get message from mailbox │ │
│ │ │ │
│ │ Match message type: │ │
│ │ CreateSession Create new session │ │
│ │ CloseSession Remove session │ │
│ │ WriteData Write to session │ │
│ │ Resize Resize session │ │
│ │ GridAck Open ACK gate │ │
│ │ } │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Actor Pattern Benefits
┌─────────────────────────────────────────────────────────────────┐
│ ACTOR BENEFITS │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Frontend Tauri Commands SessionActor │
│ (TypeScript) (multiple threads) (single owner) │
│ │
│ ┌─────────┐ │
│ │ New Tab │────┐ │
│ └─────────┘ │ │
│ │ ┌──────────┐ ┌─────────────────┐ │
│ ┌─────────┐ ├─────▶│ MPSC │──────▶│ SessionActor │ │
│ │ Resize │────┤ │ Channel │ │ │ │
│ └─────────┘ │ └──────────┘ │ sessions: {...} │ │
│ │ │ ↑ │ │
│ ┌─────────┐ │ │ Only accessible │ │
│ │Close Tab│────┘ │ from here! │ │
│ └─────────┘ └─────────────────┘ │
│ │
│ ┌─────────┐ │ │
│ │PTY Data │─────────────────────────────────────┘ │
│ └─────────┘ │
│ │
│ ═══════════════════════════════════════════════════════════ │
│ │
│ Actor Benefits: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ No lock contention (single owner) │ │
│ │ No deadlocks (no locks!) │ │
│ │ Guaranteed ordering (MPSC channel) │ │
│ │ Easy to reason about (one thread owns state) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Why Rust?
The patterns work in any language, but Rust provides additional benefits.┌─────────────────────────────────────────────────────────────────┐
│ WHY RUST FOR MONOTERM │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Patterns work anywhere: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ JavaScript Python Go Rust │ │
│ │ ✓ ✓ ✓ ✓ │ │
│ │ │ │
│ │ Actor pattern and ACK flow control work in all │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Where Rust matters: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. VTE Parsing (Alacritty) │ │
│ │ Called thousands of times per second │ │
│ │ GC would cause stuttering │ │
│ │ │ │
│ │ 2. Cell Processing │ │
│ │ 120 cols x 40 rows = 4,800 cells │ │
│ │ Needs native speed │ │
│ │ │ │
│ │ 3. PTY I/O │ │
│ │ Unix socket communication │ │
│ │ Needs system-level access │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Architecture choice: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Option A: VTE(Rust) → IPC → SessionActor(JS) → IPC │ │
│ │ │ │
│ │ Option B: VTE(Rust) + SessionActor(Rust) → IPC │ │
│ │ ↑ MonoTerm uses this │ │
│ │ │ │
│ │ IPC count: A = 2 times, B = 1 time │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Complete Architecture
┌─────────────────────────────────────────────────────────────────┐
│ PATTERNS ACROSS UNIX SOCKET │
├─────────────────────────────────────────────────────────────────┤
│ │
│ PTY Daemon Unix Socket Tauri Backend │
│ ────────── ─────────── ───────────── │
│ │
│ ┌─────────┐ ┌─────────────┐ │
│ │ Shell │ │SessionActor │ │
│ │ bash │────────────────────────────────▶ │ │ │
│ │ zsh │ Raw bytes │ Sessions │ │
│ │ fish │ │ Workers │ │
│ └─────────┘ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ VTE Parser │ │
│ │ (Alacritty) │ │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ Tauri IPC ┌─────────────┐ │
│ Frontend ◀────────────────────────────────│ Cell Convert│ │
│ (xterm.js WebGL) │ + DiffHint │ │
│ └─────────────┘ │
│ │
│ Pattern Summary: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ SessionActor: Lock-free concurrency via MPSC │ │
│ │ ACK Flow: Frontend-driven backpressure │ │
│ │ Sidecar: PTY daemon survives app crash │ │
│ │ DiffHint: Intelligent partial rendering │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Summary
┌─────────────────────────────────────────────────────────────────┐
│ DESIGN PATTERNS SUMMARY │
├─────────────────────────────────────────────────────────────────┤
│ │
│ SessionActor / ACK Flow Control: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Level: Design Pattern (language-independent) │ │
│ │ Origin: Erlang (Actor), TCP (ACK) │ │
│ │ Rust required? NO │ │
│ │ Works in JS/Python/Go? YES │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Where Rust is critical: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ VTE Parsing (Alacritty) Performance critical │ │
│ │ PTY I/O (Unix socket) System level │ │
│ │ Cell Processing Better without GC │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Why this architecture: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ VTE parsing is in Rust anyway │ │
│ │ SessionActor in Rust reduces IPC overhead │ │
│ │ Patterns are high-level, placement determines perf │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘