Skip to main content

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.

Smooth Scroll

MonoTerm implements pixel-level scroll accumulation for fluid scrolling experience, especially on trackpads.

The Problem: Row-Based Scrolling

Traditional terminals scroll by whole rows. This creates jerky movement on trackpads that send sub-row pixel deltas.
╔═══════════════════════════════════════════════════════════════════════════════╗
║  TRADITIONAL ROW-BASED SCROLLING                                              ║
╠═══════════════════════════════════════════════════════════════════════════════╣
║                                                                               ║
║  Trackpad sends: deltaY = 3px, 5px, 2px, 4px, 6px...                         ║
║                                                                               ║
║  Row height = 20px                                                            ║
║                                                                               ║
║  ┌─────────────────────────────────────────────────────────────────────────┐  ║
║  │                                                                         │  ║
║  │  Input:   3px   5px   2px   4px   6px   (total: 20px)                   │  ║
║  │            │     │     │     │     │                                    │  ║
║  │            ▼     ▼     ▼     ▼     ▼                                    │  ║
║  │  Result:  NONE  NONE  NONE  NONE  JUMP 1 ROW                            │  ║
║  │                                                                         │  ║
║  │           ────────────────────────┬────                                 │  ║
║  │                   Nothing         │ Sudden jump                         │  ║
║  │                                                                         │  ║
║  └─────────────────────────────────────────────────────────────────────────┘  ║
║                                                                               ║
║  User experience: Laggy, then sudden jump                                     ║
║                                                                               ║
╚═══════════════════════════════════════════════════════════════════════════════╝

The Solution: Pixel Accumulation

MonoTerm accumulates sub-row pixel deltas until a full row is reached.
╔═══════════════════════════════════════════════════════════════════════════════╗
║  PIXEL ACCUMULATION SCROLLING                                                 ║
╠═══════════════════════════════════════════════════════════════════════════════╣
║                                                                               ║
║  Trackpad sends: deltaY = 3px, 5px, 2px, 4px, 6px...                         ║
║                                                                               ║
║  Row height = 20px                                                            ║
║                                                                               ║
║  ┌─────────────────────────────────────────────────────────────────────────┐  ║
║  │                                                                         │  ║
║  │  pendingY accumulator:                                                  │  ║
║  │                                                                         │  ║
║  │  Input:    3px    5px    2px    4px    6px                              │  ║
║  │             │      │      │      │      │                               │  ║
║  │             ▼      ▼      ▼      ▼      ▼                               │  ║
║  │  pendingY:  3  →   8  →  10  →  14  →  20                               │  ║
║  │             │      │      │      │      │                               │  ║
║  │             ▼      ▼      ▼      ▼      ▼                               │  ║
║  │  Action:  wait   wait   wait   wait   SCROLL 1 ROW                      │  ║
║  │                                       pendingY = 0                      │  ║
║  │                                                                         │  ║
║  └─────────────────────────────────────────────────────────────────────────┘  ║
║                                                                               ║
║  User experience: Smooth, predictable scrolling                               ║
║                                                                               ║
╚═══════════════════════════════════════════════════════════════════════════════╝

State Tracking

The scroll state tracks accumulated pixel delta (pendingY), current row height, and animation status. When accumulated pixels exceed one row height, scrolling triggers and the remainder carries over.

Trackpad vs Mouse Detection

MonoTerm detects input device type and applies different scroll behavior.
╔═══════════════════════════════════════════════════════════════════════════════╗
║  INPUT DEVICE DETECTION                                                       ║
╠═══════════════════════════════════════════════════════════════════════════════╣
║                                                                               ║
║  Detection logic:                                                             ║
║  ─────────────────                                                            ║
║                                                                               ║
║  isPrecision = Math.abs(ev.deltaY) < 10 &&                                    ║
║                ev.deltaMode === WheelEvent.DOM_DELTA_PIXEL                    ║
║                                                                               ║
║                                                                               ║
║  ┌─────────────────────────────┐    ┌─────────────────────────────┐          ║
║  │                             │    │                             │          ║
║  │  TRACKPAD (isPrecision)     │    │  MOUSE WHEEL (!isPrecision) │          ║
║  │                             │    │                             │          ║
║  │  deltaY: 1-9 pixels         │    │  deltaY: 100+ pixels        │          ║
║  │  deltaMode: PIXEL           │    │  deltaMode: LINE or PAGE    │          ║
║  │                             │    │                             │          ║
║  │  ┌───────────────────────┐  │    │  ┌───────────────────────┐  │          ║
║  │  │ Pixel accumulation    │  │    │  │ Direct row scroll     │  │          ║
║  │  │ + Bezier animation    │  │    │  │ (traditional behavior)│  │          ║
║  │  └───────────────────────┘  │    │  └───────────────────────┘  │          ║
║  │                             │    │                             │          ║
║  └─────────────────────────────┘    └─────────────────────────────┘          ║
║                                                                               ║
╚═══════════════════════════════════════════════════════════════════════════════╝

Bezier Animation

For smooth deceleration, MonoTerm applies easing functions to scroll animation.
╔═══════════════════════════════════════════════════════════════════════════════╗
║  BEZIER EASING FUNCTIONS                                                      ║
╠═══════════════════════════════════════════════════════════════════════════════╣
║                                                                               ║
║  Available easing curves:                                                     ║
║                                                                               ║
║                                                                               ║
║  ease-out (default)              ease-in                 ease-in-out          ║
║  ─────────────────               ────────                ───────────          ║
║  f(t) = 1 - (1-t)³               f(t) = t³               cubic-bezier         ║
║                                                                               ║
║       │    xxxxxxx                    │         x             │      xxx      ║
║       │  xx                           │        x              │    xx   xx    ║
║       │ x                             │       x               │   x       x   ║
║       │x                              │     xx                │  x         x  ║
║       │x                              │   xx                  │ x           x ║
║       │x                              │ xx                    │x             x║
║       └───────────                    └───────────            └───────────    ║
║                                                                               ║
║  Fast start,                     Slow start,              Smooth both         ║
║  smooth stop                     fast end                 ends                ║
║                                                                               ║
╚═══════════════════════════════════════════════════════════════════════════════╝

Configuration

Users can customize scroll behavior via settings:
SettingDefaultDescription
terminalSmoothScrollDuration150msAnimation duration
terminalSmoothScrollEasingease-outEasing function
╔═══════════════════════════════════════════════════════════════════════════════╗
║  SCROLL BEHAVIOR FLOW                                                         ║
╠═══════════════════════════════════════════════════════════════════════════════╣
║                                                                               ║
║  Wheel Event                                                                  ║
║       │                                                                       ║
║       ▼                                                                       ║
║  ┌─────────────────────┐                                                      ║
║  │ Check alt buffer    │──── YES ───▶ Direct scroll (vim/tmux mode)          ║
║  └─────────┬───────────┘                                                      ║
║            │ NO                                                               ║
║            ▼                                                                  ║
║  ┌─────────────────────┐                                                      ║
║  │ isPrecision?        │──── NO ────▶ Traditional row scroll                 ║
║  └─────────┬───────────┘                                                      ║
║            │ YES (trackpad)                                                   ║
║            ▼                                                                  ║
║  ┌─────────────────────┐                                                      ║
║  │ Add to pendingY     │                                                      ║
║  └─────────┬───────────┘                                                      ║
║            │                                                                  ║
║            ▼                                                                  ║
║  ┌─────────────────────┐                                                      ║
║  │ |pendingY| >= row?  │──── NO ────▶ Wait for more input                    ║
║  └─────────┬───────────┘                                                      ║
║            │ YES                                                              ║
║            ▼                                                                  ║
║  ┌─────────────────────┐                                                      ║
║  │ Calculate rows      │                                                      ║
║  │ rows = pendingY/h   │                                                      ║
║  └─────────┬───────────┘                                                      ║
║            │                                                                  ║
║            ▼                                                                  ║
║  ┌─────────────────────┐                                                      ║
║  │ Animate scroll      │                                                      ║
║  │ (Bezier easing)     │                                                      ║
║  └─────────┬───────────┘                                                      ║
║            │                                                                  ║
║            ▼                                                                  ║
║  ┌─────────────────────┐                                                      ║
║  │ pendingY = remainder│                                                      ║
║  └─────────────────────┘                                                      ║
║                                                                               ║
╚═══════════════════════════════════════════════════════════════════════════════╝

Alt Buffer Bypass

When in alternate screen mode (vim, tmux, less), smooth scroll is bypassed for native app handling.
╔═══════════════════════════════════════════════════════════════════════════════╗
║  ALT BUFFER DETECTION                                                         ║
╠═══════════════════════════════════════════════════════════════════════════════╣
║                                                                               ║
║  Normal buffer (shell):                                                       ║
║  ──────────────────────                                                       ║
║  ┌─────────────────────────────────────┐                                      ║
║  │ $ ls -la                            │                                      ║
║  │ total 48                            │  ← Smooth scroll enabled             ║
║  │ drwxr-xr-x  12 user  staff   384    │                                      ║
║  │ -rw-r--r--   1 user  staff  1024    │                                      ║
║  └─────────────────────────────────────┘                                      ║
║                                                                               ║
║                                                                               ║
║  Alt buffer (vim/tmux):                                                       ║
║  ──────────────────────                                                       ║
║  ┌─────────────────────────────────────┐                                      ║
║  │ ~                                   │                                      ║
║  │ ~ VIM - file.txt                    │  ← Direct scroll to app             ║
║  │ ~                                   │    (vim handles scrolling)          ║
║  │ -- INSERT --                        │                                      ║
║  └─────────────────────────────────────┘                                      ║
║                                                                               ║
║                                                                               ║
║  Detection: term.buffer.active.type === 'alternate'                           ║
║                                                                               ║
╚═══════════════════════════════════════════════════════════════════════════════╝

Implementation

The wheel event handler follows the flow diagram above: check alt buffer, detect precision input, accumulate or direct scroll. The complete flow is described in the “Scroll Behavior Flow” diagram.

Sync Rendering

Immediate render for flicker-free display

xterm.js Renderer

How xterm.js is used as display component