Skip to main content

Overview

OpenTUI provides a comprehensive keyboard input handling system that supports standard key events, modifier keys, and the Kitty keyboard protocol for enhanced terminal input.

KeyHandler

The KeyHandler class is the main entry point for handling keyboard input. It extends EventEmitter and emits events for key presses, key releases, and paste operations.

Events

keypress
event
Emitted when a key is pressed down.Callback arguments:
  • event (KeyEvent) - The key event object
keyrelease
event
Emitted when a key is released.Callback arguments:
  • event (KeyEvent) - The key event object
paste
event
Emitted when text is pasted into the terminal.Callback arguments:

Usage

import { createCliRenderer } from '@opentui/core'

const renderer = await createCliRenderer()

// Listen for key presses
renderer.keyInput.on('keypress', (event) => {
  console.log(`Key pressed: ${event.name}`)
  
  if (event.ctrl && event.name === 's') {
    console.log('Save shortcut triggered')
    event.preventDefault()
  }
})

// Listen for key releases
renderer.keyInput.on('keyrelease', (event) => {
  console.log(`Key released: ${event.name}`)
})

// Listen for paste events
renderer.keyInput.on('paste', (event) => {
  console.log(`Pasted text: ${event.text}`)
})

KeyEvent

The KeyEvent interface represents a keyboard event with detailed information about the key pressed and any modifiers.

Properties

name
string
The name of the key (e.g., “a”, “return”, “escape”, “up”, “f1”)
ctrl
boolean
Whether the Ctrl/Control modifier key was pressed
meta
boolean
Whether the Meta/Alt/Option modifier key was pressed
shift
boolean
Whether the Shift modifier key was pressed
option
boolean
Whether the Option/Alt modifier key was pressed (macOS)
super
boolean
Whether the Super/Command modifier key was pressed (requires Kitty protocol)
hyper
boolean
Whether the Hyper modifier key was pressed (requires Kitty protocol)
sequence
string
The raw escape sequence that was received
number
boolean
Whether the key is a numeric digit (0-9)
raw
string
The original raw input string
eventType
KeyEventType
The type of keyboard event: "press", "repeat", or "release"
source
'raw' | 'kitty'
The source parser that decoded this event
code
string
The terminal escape code (if applicable)
capsLock
boolean
Whether Caps Lock was active (requires Kitty protocol)
numLock
boolean
Whether Num Lock was active (requires Kitty protocol)
baseCode
number
The base key code before modifiers (Kitty protocol)
repeated
boolean
Whether this is a repeated key event (key held down)
defaultPrevented
boolean
Whether preventDefault() was called on this event
propagationStopped
boolean
Whether stopPropagation() was called on this event

Methods

preventDefault()
() => void
Prevents the default action for this key event from executing
stopPropagation()
() => void
Stops the event from propagating to other handlers

Example

renderer.keyInput.on('keypress', (event) => {
  // Check for specific key combinations
  if (event.ctrl && event.shift && event.name === 'p') {
    console.log('Command palette opened')
    event.preventDefault()
    event.stopPropagation()
  }
  
  // Handle arrow keys
  if (['up', 'down', 'left', 'right'].includes(event.name)) {
    console.log(`Arrow key: ${event.name}`)
  }
  
  // Handle numbers
  if (event.number) {
    console.log(`Number key: ${event.name}`)
  }
})

PasteEvent

The PasteEvent interface represents a paste operation in the terminal (bracketed paste mode).

Properties

text
string
The pasted text content (ANSI escape codes are stripped)
defaultPrevented
boolean
Whether preventDefault() was called on this event
propagationStopped
boolean
Whether stopPropagation() was called on this event

Methods

preventDefault()
() => void
Prevents the default paste handling
stopPropagation()
() => void
Stops the event from propagating to other handlers

Example

renderer.keyInput.on('paste', (event) => {
  console.log(`User pasted: ${event.text.substring(0, 50)}...`)
  
  // Process or validate the pasted content
  if (event.text.length > 10000) {
    console.log('Pasted text is too large')
    event.preventDefault()
  }
})

Kitty Keyboard Protocol

OpenTUI supports the Kitty keyboard protocol for enhanced keyboard input handling. This protocol provides:
  • Disambiguated escape codes (fixes ESC timing, alt+key ambiguity)
  • Event types (press, repeat, release)
  • Alternate keys (numpad vs regular, shifted vs base layout)
  • Additional modifiers (Super, Hyper, Caps Lock, Num Lock)

Configuration

Enable the Kitty keyboard protocol when creating the renderer:
const renderer = await createCliRenderer({
  useKittyKeyboard: {
    disambiguate: true,      // Default: true
    alternateKeys: true,     // Default: true
    events: false,           // Default: false (enables keyrelease events)
    allKeysAsEscapes: false, // Default: false
    reportText: false,       // Default: false
  }
})

Options

disambiguate
boolean
default:"true"
Disambiguate escape codes to fix ESC timing issues and alt+key ambiguity
alternateKeys
boolean
default:"true"
Report alternate keys (numpad, shifted, base layout) for cross-keyboard shortcuts
events
boolean
default:"false"
Report event types (press, repeat, release) to enable keyrelease events
allKeysAsEscapes
boolean
default:"false"
Report all keys as escape codes
reportText
boolean
default:"false"
Report text associated with key events

Common Key Names

Special Keys

  • "return" - Enter/Return key
  • "escape" - Escape key
  • "backspace" - Backspace key
  • "tab" - Tab key
  • "space" - Spacebar
  • "delete" - Delete key
  • "up", "down", "left", "right" - Arrow keys
  • "home", "end" - Home and End keys
  • "pageup", "pagedown" - Page Up and Page Down

Function Keys

  • "f1" through "f12" - Function keys

Letters and Numbers

  • "a" through "z" - Letter keys (lowercase)
  • "0" through "9" - Number keys

Best Practices

Call event.stopPropagation() when you handle an event to prevent it from bubbling to other handlers:
renderer.keyInput.on('keypress', (event) => {
  if (event.ctrl && event.name === 'q') {
    process.exit(0)
    event.stopPropagation()
  }
})
Before performing an action, check if another handler already prevented the default behavior:
renderer.keyInput.on('keypress', (event) => {
  if (event.defaultPrevented) return
  
  // Handle the event
})
Follow common keyboard shortcut conventions:
  • Ctrl+C: Copy (or exit if enabled in config)
  • Ctrl+V: Paste
  • Ctrl+Z: Undo
  • Ctrl+S: Save

Build docs developers (and LLMs) love