Skip to content

Input parser

The InputParser sits between raw stdin and your app. It reads bytes off the stream, decodes escape sequences, and fires typed KeyEvent and MouseEvent callbacks.

The App creates one for you, but you have direct access if you need lower-level control.

Usage

·CODE
import { InputParser } from '@termuijs/core'

const parser = new InputParser(process.stdin)

parser.onKey((event) => {
    console.log(event.key, event.ctrl, event.shift, event.alt)
})

parser.onMouse((event) => {
    console.log(event.x, event.y, event.button)
})

// Start reading from stdin
parser.start()

// Later, stop reading
parser.stop()

KeyEvent

·CODE
interface KeyEvent {
    key: string        // 'a', 'enter', 'up', 'escape', etc.
    ctrl: boolean      // Ctrl was held
    shift: boolean     // Shift was held
    alt: boolean       // Alt/Option was held
    raw: Buffer        // The original bytes from stdin
}

Supported keys

CategoryKeys
PrintableAll ASCII characters
Navigationup, down, left, right, home, end, pageup, pagedown
Controlenter, tab, escape, backspace, delete, space
Functionf1 through f12
Modifiersctrl+*, shift+*, alt+*

API

MethodWhat it does
onKey(handler)Register a key event callback. Returns unsubscribe function.
onMouse(handler)Register a mouse event callback. Returns unsubscribe function.
onPaste(handler)Register a bracketed-paste callback. Returns unsubscribe function.
onFocusChange(handler)Register a terminal focus-in/focus-out callback.
start()Begin reading from stdin.
stop()Stop reading. Can be restarted later.

Bracketed paste

When bracketed paste mode is active, the terminal wraps pasted text in \x1b[200~ and \x1b[201~ markers. The parser detects those markers and fires a paste event with the raw text, instead of treating each character as a key press.

Enable it via the Terminal option or the App constructor, then subscribe:

·CODE
const parser = new InputParser(process.stdin)

parser.onPaste((text) => {
    console.log('User pasted:', text)
})

The App forwards paste events through app.events.on('paste', handler).

Mouse gestures

MouseGestures synthesizes higher-level events from the raw mousedown, mousemove, and mouseup stream. Import and use it alongside InputParser:

·CODE
import { InputParser, MouseGestures } from '@termuijs/core'

const parser = new InputParser(process.stdin)
const gestures = new MouseGestures({ doubleClickMs: 300 })

parser.onMouse((event) => {
    const synthesized = gestures.feed(event)
    for (const e of synthesized) {
        // e.type is 'dblclick', 'drag', or 'dragend'
        console.log(e.type, e.x, e.y)
    }
})

Synthesized event types:

TypeWhen it fires
dblclickTwo mousedown events at the same position within doubleClickMs
dragEvery mousemove while a button is held
dragendThe mouseup that ends a drag

Mouse event modifiers

Raw MouseEvent objects now include modifier key state from the SGR escape sequence:

·CODE
parser.onMouse((event) => {
    console.log(event.shift, event.alt, event.ctrl)
    // Horizontal scroll: event.scrollAxis === 'horizontal', event.scrollDeltaX
    // Vertical scroll:   event.scrollAxis === 'vertical',   event.scrollDelta
})

Horizontal scroll-wheel events are decoded from SGR button bits 6 and 7 and arrive as type: 'scroll' with scrollAxis: 'horizontal' and a scrollDeltaX of -1 or 1.

Key chords

ChordMatcher lets you bind multi-key sequences, similar to vim-style bindings. Create one instance, bind sequences, then feed each KeyEvent to it:

·CODE
import { ChordMatcher } from '@termuijs/core'

const chords = new ChordMatcher({ timeoutMs: 800 })

// Bind 'g g' to jump to the top
const unsub = chords.bind(['g', 'g'], () => {
    scrollToTop()
})

parser.onKey((event) => {
    // Returns true if the event was consumed by a chord
    const consumed = chords.feed(event)
    if (!consumed) handleNormalKey(event)
})

// Remove the binding later
unsub()

Chords reset automatically if the next key doesn't match within timeoutMs milliseconds (default 800 ms).