SkillAgentSearch skills...

Rcurses

An alternative curses library written in pure Ruby

Install / Use

/learn @isene/Rcurses
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

rcurses - An alternative curses library written in pure Ruby

Ruby Gem Version Unlicense Stay Amazing

<img src="img/rcurses-logo.png" width="150" height="150">

Create curses applications for the terminal easier than ever.

Here's a somewhat simple example of a TUI program using rcurses: The T-REX calculator.

And here's a much more involved example: The RTFM terminal file manager.

What's new in 6.2.0

  • Popup widget - New Rcurses::Popup class for modal dialogs with auto-centering, border, scrolling, and built-in key handling (see Popup section below)
  • Built-in emoji picker - Panes with emoji = true get a Ctrl-E shortcut in editline that opens an emoji overlay with category tabs, search, and cursor-positioned grid rendering
  • Pane color cache invalidation - Changing fg or bg on a Pane now forces a full repaint automatically (no more stale diff-rendering artifacts)
  • Startup stdin flush - Rcurses.init! now flushes pending stdin data (cursor position responses) to prevent first-keypress blocking
  • Pane update flag - Set pane.update = false to skip rendering during batch operations
  • Wider ESC timeout - Escape sequence detection timeout increased from 50ms to 150ms for better terminal compatibility
  • Unicode display_width fixes - Improved handling of FE0F variation selectors, ZWJ sequences, and dingbat characters
  • Editline improvements - Unicode-aware padding, content truncation, multiline paste joins all lines
<img src="img/rcurses-emoji.png" width="400">

Previous notable versions: 6.1.1 (error logging), 6.1.0 (safe ANSI methods), 6.0.0 (explicit init!), 5.0.0 (memory fixes), 4.5 (RGB colors)

Why?

Having struggled with the venerable curses library and the ruby interface to it for many years, I finally got around to write an alternative - in pure Ruby.

Design principles

Simple and with minimum of external dependencies.

Installation

Simply run gem install rcurses.

To use this library do:

require 'rcurses'

Features

  • Create panes (with the colors and(or border), manipulate the panes and add content
  • Dress up text (in panes or anywhere in the terminal) in bold, italic, underline, reverse color, blink and in any 256 terminal colors for foreground and background
  • Use a simple editor to let users edit text in panes
  • Left, right or center align text in panes
  • Cursor movement around the terminal

The elements

rcurses gives you the following elements:

  • The class Pane to create and manilpulate panes/boxes
  • Extensions to the class String to print text in various degrees of fancy and also strip any fanciness
  • A module Cursor to give you cursor movements around the terminal
  • A module Rinput providing the function getchr to capture a single character input from the user (much better than any Ruby built-ins)

class Pane

To create a pane do something like this:

mypane = Rcurses::Pane.new(80, 30, 30, 10, 19, 229)

This will create a pane/box starting at terminal column/x 80 and row/y 30 with the width of 30 characters and a hight of 10 characters and with the foreground color 19 and background color 229 (from the 256 choices available)

The format for creating a pane is:

Rcurses::Pane.new(x, y, w, h, fg, bg)

You can drop the last two 256-color codes to create a pane with the defaults for your terminal.

By adding values for the terminal size in your program:

@max_h, @max_w = IO.console.winsize

...you can use these values to create proportinally sized panes. So, a hight value of "@max_h/2" is valid to create a pane with the height of half the terminal height (the integer corresponding to half the terminal height will then be accessible as the variable h). Use the variables @max_h for terminal height and @max_w for terminal width.

Avaliable properties/variables:

Property | Description ---------------|--------------------------------------------------------------- x | The x (column) position of the Pane y | The y (row) position of the Pane w | The width of the Pane h | The heigth of the Pane fg | Foreground color for the Pane bg | Background color for the Pane border | Draw border around the Pane (=true) or not (=false), default being false scroll | Whether to indicate more text to be shown above/below the Pane, default is true scroll_fg | Color for scroll indicators (∆/∇), defaults to pane fg color if nil text | The text/content of the Pane ix | The line number at the top of the Pane, starts at 0, the first line of text in the Pane index | An attribute that can be used to track the selected line/element in the pane align | Text alignment in the Pane: "l" = lefts aligned, "c" = center, "r" = right, with the default "l" prompt | The prompt to print at the beginning of a one-liner Pane used as an input box moreup | Set to true when there is more text above what is shown (top scroll bar i showing) moredown | Set to true when there is more text below what is shown (bottom scroll bar i showing) update | When false, refresh is a no-op (default: true). Useful during batch operations emoji | When true, Ctrl-E in editline opens the built-in emoji picker (default: false) emoji_refresh | Optional lambda/proc called after emoji picker closes, to redraw the UI

The methods for Pane:

Method | Description ---------------|--------------------------------------------------------------- new/init | Initializes a Pane with optional arguments x, y, w, h, fg and bg move(x,y) | Move the pane by xand y (mypane.move(-4,5) will move the pane left four characters and five characters down) refresh | Refreshes/redraws the Pane with content border_refresh | Refresh the Pane border only - CRITICAL: Use this after changing border property full_refresh | Refreshes/redraws the Pane with content completely (without diff rendering) edit | An editor for the Pane. When this is invoked, all existing font dressing is stripped and the user gets to edit the raw text. The user can add font effects similar to Markdown; Use an asterisk before and after text to be drawn in bold, text between forward-slashes become italic, and underline before and after text means the text will be underlined, a hash-sign before and after text makes the text reverse colored. You can also combine a whole set of dressings in this format: <23,245,biurl\|Hello World!> - this will make "Hello World!" print in the color 23 with the background color 245 (regardless of the Pane's fg/bg setting) in bold, italic, underlined, reversed colored and blinking. Hitting ESC while in edit mode will disregard the edits, while Ctrl-S will save the edits editline | Used for one-line Panes. It will print the content of the property prompt and then the property text that can then be edited by the user. Hitting ESC will disregard the edits, while ENTER will save the edited text clear | Clears the pane cleanup | Cleans up pane memory (history, caches) - useful for memory management say(text) | Short form for setting panel.text, then doing a refresh of that panel ask(prompt,text) | Short form of setting panel.prompt, then panel.text, doing a panel.editline and then returning panel.text pagedown | Scroll down one page height in the text (minus one line), but not longer than the length of the text pageup | Scroll up one page height in the text (minus one line) linedown | Scroll down one line in the text lineup | Scroll up one line in the text bottom | Scroll to the bottom of the text in the pane top | Scroll to the top of the text in the pane

class Popup

A reusable popup overlay widget for modal dialogs, selection menus, and informational displays. Extends Pane with auto-centering, built-in scrolling, and keyboard handling.

# Auto-centered popup with default size
popup = Rcurses::Popup.new(w: 50, h: 20)

# Explicit position
popup = Rcurses::Popup.new(x: 10, y: 5, w: 50, h: 20, fg: 255, bg: 236)

Modal usage (blocks until ESC or ENTER):

result = popup.modal(content_string)
# Returns selected line index on ENTER, or nil on ESC
# Built-in: UP/DOWN/PgUP/PgDN/HOME/END for scrolling

Custom key handling:

result = popup.modal(content) do |chr, index|
  case chr
  when 'd'
    delete_item(index)
    :dismiss        # Close popup
  when 'e'
    "edit:#{index}" # Return custom value
  end
end

Manual control (non-blocking):

popup.show(content_string)
# ... your own input loop ...
popup.dismiss(refresh_panes: [pane1, pane2])  # Clears area, refreshes underlying panes

Emoji Picker

Panes can opt in to a built-in emoji picker that opens with Ctrl-E during editline:

pane.emoji = true                   # Enable Ctrl-E shortcut
pane.emoji_refresh = -> { render }  # Optional: redraw UI after picker closes
pane.editline                       # Ctrl-E now opens emoji overlay

The picker features category tabs (Tab/Shift-Tab), arrow navigation, search-by-keyword, and cursor-positioned grid rendering for pixel-perfect alignment regardless of Unicode width variations.

class String extensions

Method extensions provided for the class String.

A color can either be an integer in the range 0-255 for the usual 256 colors in a terminal, or it can be a string representing RGB. So both of these are valid: string.fg(219) and string.fg("4d22a0").

Method | Description ---------------|----------------------

View on GitHub
GitHub Stars26
CategoryDevelopment
Updated7d ago
Forks3

Languages

Ruby

Security Score

90/100

Audited on Mar 29, 2026

No findings