Skip to main content

Overview

OpenTUI provides comprehensive mouse input handling for terminal user interfaces, supporting clicks, drags, scrolling, and hover events.

MouseEvent

The MouseEvent class represents a mouse interaction event with position, button information, and modifiers.

Properties

type
MouseEventType
The type of mouse event:
  • "down" - Mouse button pressed
  • "up" - Mouse button released
  • "move" - Mouse moved without buttons pressed
  • "drag" - Mouse moved with button pressed
  • "drag-end" - Drag operation ended
  • "drop" - Item dropped on target
  • "over" - Mouse entered element
  • "out" - Mouse left element
  • "scroll" - Mouse wheel scrolled
button
number
The mouse button that triggered the event:
  • 0 (MouseButton.LEFT) - Left button
  • 1 (MouseButton.MIDDLE) - Middle button
  • 2 (MouseButton.RIGHT) - Right button
  • 4 (MouseButton.WHEEL_UP) - Scroll wheel up
  • 5 (MouseButton.WHEEL_DOWN) - Scroll wheel down
x
number
The X coordinate of the mouse pointer (0-based, terminal columns)
y
number
The Y coordinate of the mouse pointer (0-based, terminal rows)
target
Renderable | null
The renderable element that was clicked or is under the cursor
source
Renderable
The source renderable for drag-and-drop operations (available in "drop" and "over" events during drag)
modifiers
object
Keyboard modifiers held during the mouse event
scroll
ScrollInfo
Scroll information (only present for "scroll" events)
isDragging
boolean
Whether this event is part of an active drag operation
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 mouse event from executing (e.g., prevents focus change on click)
stopPropagation()
() => void
Stops the event from propagating to parent elements

Example

import { Renderable } from '@opentui/core'

class Button extends Renderable {
  constructor() {
    super()
    
    this.on('mouse:down', (event) => {
      if (event.button === MouseButton.LEFT) {
        console.log(`Button clicked at (${event.x}, ${event.y})`)
        event.preventDefault()
        event.stopPropagation()
      }
    })
    
    this.on('mouse:over', (event) => {
      console.log('Mouse entered button')
      this.setStyle({ backgroundColor: 'blue' })
    })
    
    this.on('mouse:out', (event) => {
      console.log('Mouse left button')
      this.setStyle({ backgroundColor: 'gray' })
    })
  }
}

MouseButton Enum

The MouseButton enum provides constants for mouse button identification:
enum MouseButton {
  LEFT = 0,
  MIDDLE = 1,
  RIGHT = 2,
  WHEEL_UP = 4,
  WHEEL_DOWN = 5,
}

Example

this.on('mouse:down', (event) => {
  switch (event.button) {
    case MouseButton.LEFT:
      console.log('Left click')
      break
    case MouseButton.RIGHT:
      console.log('Right click')
      break
    case MouseButton.MIDDLE:
      console.log('Middle click')
      break
  }
})

Mouse Events on Renderables

Renderables can listen to mouse events using the following event names:
mouse:down
event
Fired when a mouse button is pressed while over the renderableCallback arguments:
  • event (MouseEvent)
mouse:up
event
Fired when a mouse button is releasedCallback arguments:
  • event (MouseEvent)
mouse:move
event
Fired when the mouse moves over the renderable without any buttons pressedCallback arguments:
  • event (MouseEvent)
mouse:drag
event
Fired when the mouse moves with a button held downCallback arguments:
  • event (MouseEvent)
mouse:drag-end
event
Fired when a drag operation endsCallback arguments:
  • event (MouseEvent)
mouse:drop
event
Fired when a dragged item is dropped on the renderableCallback arguments:
  • event (MouseEvent) - event.source contains the dragged renderable
mouse:over
event
Fired when the mouse enters the renderable’s boundsCallback arguments:
  • event (MouseEvent)
mouse:out
event
Fired when the mouse leaves the renderable’s boundsCallback arguments:
  • event (MouseEvent)
mouse:scroll
event
Fired when the mouse wheel is scrolled over the renderableCallback arguments:
  • event (MouseEvent) - event.scroll contains direction and delta

Renderer Configuration

Mouse input can be configured when creating the renderer:
const renderer = await createCliRenderer({
  useMouse: true,              // Enable/disable mouse input (default: true)
  enableMouseMovement: true,   // Track mouse movement events (default: true)
  autoFocus: true,             // Auto-focus elements on click (default: true)
})

Options

useMouse
boolean
default:"true"
Enable or disable mouse input handling globally
enableMouseMovement
boolean
default:"true"
Track mouse movement and hover events (may reduce performance in some terminals)
autoFocus
boolean
default:"true"
Automatically focus renderable elements when clicked with the left mouse button

Mouse Pointer Styles

You can change the mouse cursor style using the renderer:
renderer.setMousePointer('pointer') // Show pointer cursor
renderer.setMousePointer('default') // Reset to default cursor
Available cursor styles depend on terminal support.

Drag and Drop

OpenTUI automatically handles drag and drop when you click and drag on a renderable:
class DraggableBox extends Renderable {
  constructor() {
    super()
    
    // Fired when drag starts
    this.on('mouse:drag', (event) => {
      console.log('Dragging...')
    })
    
    // Fired when drag ends
    this.on('mouse:drag-end', (event) => {
      console.log('Drag ended')
    })
  }
}

class DropTarget extends Renderable {
  constructor() {
    super()
    
    // Fired when something is dropped on this element
    this.on('mouse:drop', (event) => {
      console.log('Dropped:', event.source)
    })
    
    // Fired when something is dragged over this element
    this.on('mouse:over', (event) => {
      if (event.source) {
        console.log('Dragging over target')
      }
    })
  }
}

Scroll Handling

Handle mouse wheel scrolling:
class ScrollableList extends Renderable {
  constructor() {
    super()
    
    this.on('mouse:scroll', (event) => {
      if (event.scroll) {
        const { direction, delta } = event.scroll
        
        if (direction === 'up') {
          this.scrollUp(delta)
        } else if (direction === 'down') {
          this.scrollDown(delta)
        }
      }
    })
  }
}

Hit Testing

The renderer provides a hitTest method to determine which renderable is at a specific position:
const renderableId = renderer.hitTest(x, y)
const renderable = Renderable.renderablesByNumber.get(renderableId)

if (renderable) {
  console.log('Found renderable at position:', renderable)
}

Best Practices

Always call event.preventDefault() when you handle a click to prevent unwanted side effects:
this.on('mouse:down', (event) => {
  // Handle the click
  this.onClick()
  
  // Prevent default focus behavior
  event.preventDefault()
})
When a child element handles a mouse event, stop it from propagating to the parent:
childButton.on('mouse:down', (event) => {
  this.handleButtonClick()
  event.stopPropagation() // Don't trigger parent's click handler
})
Provide visual feedback when users hover over interactive elements:
this.on('mouse:over', (event) => {
  this.setStyle({ backgroundColor: 'highlight' })
})

this.on('mouse:out', (event) => {
  this.setStyle({ backgroundColor: 'normal' })
})
Use keyboard modifiers to provide different behaviors:
this.on('mouse:down', (event) => {
  if (event.modifiers.ctrl) {
    this.multiSelect()
  } else if (event.modifiers.shift) {
    this.rangeSelect()
  } else {
    this.singleSelect()
  }
})

Terminal Compatibility

Most modern terminals support mouse input, including:
  • iTerm2 (macOS)
  • Windows Terminal
  • Alacritty
  • Kitty
  • GNOME Terminal
  • Konsole
  • tmux (with mouse mode enabled)
Some legacy terminals may have limited or no mouse support.

Build docs developers (and LLMs) love