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.
The Problem
Traditional terminals send the entire screen every time anything changes.You type one character: "a"
What changes?
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Line 1: ################################ (unchanged) β
β Line 2: ################################ (unchanged) β
β Line 3: ################################ (unchanged) β
β ... β
β Line 39: ################################ (unchanged) β
β Line 40: $ command_here| (ONE CHAR!) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Traditional terminal sends: ALL 40 lines = ~50,000 bytes
Actual change: 1 character = ~25 bytes
Wasted: 99.95% of data!
MonoTerm Solution: Hash-Based Diffing
Each line gets a unique fingerprint (hash). Only lines with changed hashes are transmitted.
How It Works
Each line in the terminal buffer is assigned a 64-bit hash computed from its content:Line Content Hash
βββββββββββ ββββ
"Hello World" -> 0xA3F2B71C
"$ ls -la" -> 0x8C4D91E5
"file.txt 1024" -> 0x2B7A03F9
| Old Hash | New Hash | Result |
|---|---|---|
| 0xA3F2B71C | 0xA3F2B71C | SAME (skip!) |
| 0x8C4D91E5 | 0x8C4D91E5 | SAME (skip!) |
| 0x2B7A03F9 | 0x5E1C82A4 | DIFFERENT (send!) |
Hash Algorithm: FNV-1a
MonoTerm uses FNV-1a (Fowler-Noll-Vo), a fast non-cryptographic hash optimized for short strings like terminal lines.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β LINE FINGERPRINTING β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Each line becomes a unique 64-bit fingerprint: β
β β
β "$ ls -la" + [green] + [bold] β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β FNV-1a Hash β β
β β βββββββββββββββββββββββββββββββββ β β
β β β’ Combines: characters + colors + attributes β β
β β β’ Fast: ~5 GB/s on modern CPUs β β
β β β’ Low collision probability (1 in 18 quintillion) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β OUTPUT: 0x8C4D91E52B7A03F9 (64-bit fingerprint) β
β β
β Same content β Same fingerprint (always!) β
β Different content β Different fingerprint β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Why FNV-1a?
| Algorithm | Verdict | Reason |
|---|---|---|
| FNV-1a | Used | Extremely fast, simple, great for short strings |
| SHA-256 | Not used | Too slow, cryptographic overkill |
| xxHash | Not used | Fast but more complex than needed |
| CRC32 | Not used | Too many collisions |
- Same line = Same fingerprint (deterministic)
- Fingerprint comparison: Single CPU instruction (O(1))
- No character-by-character comparison needed!
Line Fingerprint System
MonoTerm maintains a fingerprint for each line, enabling instant change detection.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β HOW LINE FINGERPRINTS WORK β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Each line has a unique fingerprint based on its content: β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Line: "$ ls -la" (green, bold) β β
β β β β β
β β βΌ β β
β β Fingerprint: 0x8C4D91E5 β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β What's included in fingerprint: β
β β
β β Characters ("$ ls -la") β
β β Foreground color (green) β
β β Background color (default) β
β β Text style (bold, underline, etc.) β
β β
β Comparison speed: β
β β
β Traditional: Compare 120 characters = 120 operations β
β MonoTerm: Compare 1 fingerprint = 1 operation β
β β
β Speedup: 120x faster per line! β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
DiffHint Modes
MonoTerm has four modes based on what changed:- None
- Partial
- Full
- Skip
Scenario: Only cursor moved, no content changed
- Data sent: Cursor position only (~10 bytes)
- Reduction: 99.98%
- Action: No buffer update, no render
Scenario: 1-3 lines changed (normal typing) - Most Common
- Data sent: Only dirty rows + cursor (~25-100 bytes)
- Reduction: 99.95%
- Action: Inject only changed lines, render only those rows
[skip] [skip] [skip] ... [SEND] [skip] [skip]
^
|
Only changed line
Scenario: Major screen change (clear screen, scrolling, resize)
- Data sent: Bottom 2V lines (~100,000 bytes for 40-row viewport)
- Reduction: 0% (but optimal for big changes)
- Triggers:
- Buffer size changed (resize)
- History grew significantly (fast scrolling)
- Why: Fingerprint comparison would miss changes
Scenario: Invalid frame (incomplete, corrupted)
- Data sent: Nothing
- Action: Wait for next valid frame
- Used for: Glitch protection (ybase oscillation)
Smart Comparison Range (2V)
MonoTerm only compares the bottom 2V lines (2Γ viewport height) for change detection, not the entire buffer.
Why 2V Range?
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β THE 2V COMPARISON WINDOW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Terminal buffer can have 100,000+ lines of history β
β Comparing ALL lines would be too slow β
β β
β Solution: Only compare bottom 2V (2 Γ viewport height) β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Old history (not compared) β β
β β ...thousands of lines... β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β 2V start β
β β βββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β Recent lines (compared for changes) β β β V lines β
β β β ββββββββββββββββββββββββββββββββββββββββββββββββ£ β β
β β β Viewport (what you see) β β β V lines β
β β βββββββββββββββββββββββββββββββββββββββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β For V=40 rows: Compare only bottom 80 lines β
β Instead of: 100,000 lines β
β β
β Result: Fast comparison even with huge history! β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
When Full Mode Triggers
| Condition | Why Full Mode? |
|---|---|
| Buffer size changed | Resize invalidates old fingerprints |
| History grew > V lines | Old 2V window no longer aligns |
| First frame | No previous fingerprints to compare |
The Safety Rule: Why Exactly 2V?
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β THE 2V SAFETY RULE β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£
β β
β MonoTerm uses TWO related settings: β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β β
β β COMPARISON WINDOW = 2V (bottom 80 lines for 40-row viewport) β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β This is HOW MANY lines we check for changes β β
β β β β
β β FULL MODE TRIGGER = V (history changes more than 40 lines) β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β This is WHEN we give up and refresh everything β β
β β β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β THE SAFETY RULE: β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β β
β β Full Mode Trigger < Comparison Window β β
β β (V) < (2V) β β
β β β β
β β This ensures we ALWAYS catch all changes! β β
β β β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£
β β
β WHY THIS MATTERS (Visual Proof): β
β βββββββββββββββββββββββββββββββββ β
β β
β Imagine history grows by 30 lines (less than V=40): β
β β
β BEFORE AFTER β
β ββββββββββββββββββββββββββ ββββββββββββββββββββββββββ β
β β old history β β old history β β
β β (not compared) β β + 30 new lines β β
β β β β (not compared) β β
β ββββββββββββββββββββββββββ€ ββββββββββββββββββββββββββ€ β
β β ββββββββββββββββββββ β β ββββββββββββββββββββ β β
β β β 2V WINDOW β β ββββΊ β β 2V WINDOW β β β
β β β (80 lines) β β β β (80 lines) β β β
β β β ββββββββββββββ β β β β ββββββββββββββ β β β
β β β β VIEWPORT β β β β β β VIEWPORT β β β β
β β β β (40 lines) β β β β β β (40 lines) β β β β
β β β ββββββββββββββ β β β β ββββββββββββββ β β β
β β ββββββββββββββββββββ β β ββββββββββββββββββββ β β
β ββββββββββββββββββββββββββ ββββββββββββββββββββββββββ β
β β
β β 30 < 40, so we stay in Partial mode β
β β 2V window (80 lines) covers the 30-line shift β
β β All changes are detected! β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£
β β
β What if history grows by 50 lines (more than V=40)? β
β β
β BEFORE AFTER β
β ββββββββββββββββββββββββββ ββββββββββββββββββββββββββ β
β β old history β β old history β β
β β β β + 50 new lines β β
β β β β β β
β ββββββββββββββββββββββββββ€ ββββββββββββββββββββββββββ€ β
β β ββββββββββββββββββββ β β βββββββββββββββββββββ β
β β β 2V WINDOW β β ββββΊ β β 2V WINDOW ββ β
β β β OLD POSITION β β β β NEW POSITION ββ β SHIFTED! β
β β ββββββββββββββββββββ β β βββββββββββββββββββββ β
β ββββββββββββββββββββββββββ ββββββββββββββββββββββββββ β
β β
β β 50 > 40, windows don't overlap properly β
β β Fingerprint comparison might miss changes β
β β SOLUTION: Switch to Full mode (refresh everything) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The 2V window ensures that even when content shifts, we can still detect all changes. When shifts exceed our detection range, we automatically switch to Full mode for safety.
Change Detection Flow
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β HOW MONOTERM DETECTS CHANGES β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β STEP 1: Compare fingerprints in 2V range β
β βββββββββββββββββββββββββββββββββββββββββ β
β β
β Old New β
β βββββββββββββββ βββββββββββββββ β
β β Line 1: A β βββ β Line 1: A β β SAME β
β β Line 2: B β βββ β Line 2: B β β SAME β
β β Line 3: C β β β β β Line 3: X β β CHANGED! β
β β Line 4: D β βββ β Line 4: D β β SAME β
β βββββββββββββββ βββββββββββββββ β
β β
β STEP 2: Decide mode β
β βββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Buffer size changed? βββββββββββββββ FULL β β
β β History grew > V? βββββββββββββββ FULL β β
β β No changes? βββββββββββββββ NONE β β
β β Some changes? βββββββββββββββ PARTIAL β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β STEP 3: Send only what's needed β
β βββββββββββββββββββββββββββββββ β
β β
β PARTIAL: Send only changed lines (Line 3 in example) β
β FULL: Send all lines in 2V range β
β NONE: Send nothing (cursor update only) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- 80 fingerprint comparisons (for 40-row viewport)
- Each comparison: single CPU instruction
- Total: microseconds per frame
Performance Comparison
Hash Comparison vs Character-by-Character
- Traditional
- MonoTerm
Line 1: "Hello World"
Line 2: "Hello World"
Compare: H==H, e==e, l==l, l==l, o==o, ' '==' ', ...
Operations: O(n) where n = line length
For 120-char line: 120 comparisons
For 40 lines: 4,800 comparisons per frame
Line 1 hash: 0xA3F2B71C
Line 2 hash: 0xA3F2B71C
Compare: 0xA3F2B71C == 0xA3F2B71C (single CPU instruction)
Operations: O(1) - just one comparison!
For 40 lines: 40 comparisons per frame
Real-World Examples
Scenario 1: Normal Typing
Scenario 1: Normal Typing
You type:
ls -la| Frame | Content | Mode | Data Sent |
|---|---|---|---|
| 1 | l | Partial | ~1,254 bytes |
| 2 | ls | Partial | ~1,254 bytes |
| 3 | ls | Partial | ~1,254 bytes |
| 4 | ls - | Partial | ~1,254 bytes |
| 5 | ls -l | Partial | ~1,254 bytes |
| 6 | ls -la | Partial | ~1,254 bytes |
- Total sent: 6 x 1,254 = 7,524 bytes
- Traditional: 6 x 50,000 = 300,000 bytes
- Reduction: 97.5%
Scenario 2: Command Output
Scenario 2: Command Output
You run:
ls -la in a directory with 10 files- Output: 10 lines of file listing
- Dirty rows: 10 (new lines) + 1 (prompt moved) = 11 rows
- Mode: Partial (11 < 20 threshold)
- Data sent: 11 x 1,254 = ~13,794 bytes
- Traditional: 50,000 bytes
- Reduction: 72.4%
Scenario 3: Clear Screen
Scenario 3: Clear Screen
You run:
clear- All 40 rows change (cleared + new prompt)
- Dirty rows: 40 (100%)
- Mode: Full (40 > 20 threshold)
- Data sent: 50,000 bytes
True Partial Mode
MonoTerm sends only the changed lines, not just renders them partially.
Before vs After
- Before (Render Only Partial)
- After (True Partial)
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Rust Backend: Partial mode detected β
β BUT sends ALL 1040 lines anyway β
β Data: ~50KB β
β β
β Frontend: Creates new buffer (1080 lines) β
β Copies ALL lines β
β Refreshes only changed rows β
β β
β Problem: 50KB transferred for 2 changed lines! β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Rust Backend: Partial mode detected β
β Sends ONLY 2 changed lines β
β Data: ~0.5KB β
β β
β Frontend: Reuses existing buffer β
β Updates only 2 lines β
β Refreshes only changed rows β
β β
β Result: 99% less data, no memory allocation! β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Results
| Metric | Stage 1 | Stage 2 |
|---|---|---|
| IPC data | 50KB | 0.5KB |
| Reduction | - | 99.95% |
| Buffer allocation | Recreate | Reused |
| GC pressure | High | Eliminated |
Frontend: Buffer Reuse
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β BUFFER REUSE DECISION LOGIC β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β INPUT: diffHint, linesObj, neededLength, update β
β β
β CHECKS: β
β ββββββββ β
β isPartialMode = diffHint has 'Partial' key β
β isNoneMode = diffHint === 'None' β
β bufferSizeMatches = linesObj._length === neededLength β
β β
β DECISION: β
β ββββββββββ β
β (isPartialMode || isNoneMode) && bufferSizeMatches? β
β β β
β YES ββ΄βββΊ PARTIAL/NONE MODE: Reuse existing buffer β
β β β
β ββββΊ if (update.lines.length > 0): β
β injectLines(linesObj._array, update.lines) β
β β
β NO ββββββΊ FULL MODE: Recreate entire buffer β
β β β
β ββββΊ newArray = new Array(neededLength) β
β β β
β ββββΊ for each index: newArray[i] = new BufferLine(cols) β
β β β
β ββββΊ linesObj._array = newArray β
β β β
β ββββΊ injectLines(newArray, update.lines) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ACK Gate: Flow Control
The ACK mechanism prevents buffer overflow by ensuring the frontend processes each update before receiving the next.
Backend Frontend
β β
β ββ GridUpdate (epoch=5) βββββ β
β [waiting_ack = true] β
β β
β βββββββββ ACK (epoch=5) ββββββ β
β [waiting_ack = false] β
β β
β ββ GridUpdate (epoch=6) βββββ β
β ... β
Performance Summary
By Scenario
| Scenario | DiffHint | IPC Data | Buffer | Render |
|---|---|---|---|---|
| Typing | Partial | ~0.05KB | Reused | 2.5% |
ls | Full | ~50KB | Recreated | 100% |
| Cursor blink | None | ~0.1KB | Reused | 0% |
| vim scroll | Full | ~50KB | Recreated | 100% |
Overall Reduction
| Activity | Reduction |
|---|---|
| Normal typing | 99.95% (50KB -> 25 bytes) |
| Command output | 70-90% |
| Screen clear | 0% (optimal for that case) |
| Overall average | 90%+ |
Summary
Technology
- FNV-1a hash for line fingerprinting
- 2V range comparison (bottom 80 lines)
- O(1) fingerprint comparison
- DiffHint modes: None, Partial, Full, Skip
- Smart Full mode trigger (buffer/history changes)
Benefits
- 99.95% data reduction (50KB β 25 bytes)
- Faster IPC (less data to transfer)
- Less CPU usage (less data to process)
- Better for remote connections
- Lower memory/GC pressure