Quickstart

Welcome to ratatui_ruby! This guide will help you get up and running with your first Terminal User Interface in Ruby.

Installation

Add this line to your application’s Gemfile:

gem "ratatui_ruby"

And then execute:

bundle install

Or install it yourself as:

gem install ratatui_ruby

Tutorials

Basic Application

Here is a “Hello World” application that demonstrates the core lifecycle of a ratatui_ruby app.

# 1. Initialize the terminal
RatatuiRuby.init_terminal

begin
  # The Main Loop
  loop do
    # 2. Create your UI (Immediate Mode)
    # We define a Paragraph widget inside a Block with a title and borders.
    view = RatatuiRuby::Paragraph.new(
      text: "Hello, Ratatui! Press 'q' to quit.",
      alignment: :center,
      block: RatatuiRuby::Block.new(
        title: "My Ruby TUI App",
        title_alignment: :center,
        borders: [:all],
        border_color: "cyan",
        style: { fg: "white" }
      )
    )

    # 3. Draw the UI
    RatatuiRuby.draw do |frame|
      frame.render_widget(view, frame.area)
    end

    # 4. Poll for events
    case RatatuiRuby.poll_event
    in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
      break
    else
      nil
    end
  end
ensure
  # 5. Restore the terminal to its original state
  RatatuiRuby.restore_terminal
end

quickstart_lifecycle

How it works

  1. RatatuiRuby.init_terminal: Enters raw mode and switches to the alternate screen.

  2. Immediate Mode UI: On every iteration, describe your UI by creating Data objects (e.g., Paragraph, Block).

  3. RatatuiRuby.draw { |frame| ... }: The block receives a Frame object as a canvas. Render widgets onto specific areas. Nothing is drawn until the block finishes, ensuring flicker-free updates.

  4. RatatuiRuby.poll_event: Returns a typed Event object with predicates like key?, mouse?, resize?, etc. Returns RatatuiRuby::Event::None if no events are pending. Use predicates to check event type without pattern matching.

  5. RatatuiRuby.restore_terminal: Essential for leaving raw mode and returning to the shell. Always wrap your loop in begin...ensure to guarantee this runs.

Idiomatic Session

You can simplify your code by using RatatuiRuby.run. This method handles the terminal lifecycle for you, yielding a Session object with factory methods for widgets.

RatatuiRuby.run do |tui|
  loop do
    # 2. Create your UI with methods instead of classes.
    view = tui.paragraph(
      text: "Hello, Ratatui! Press 'q' to quit.",
      alignment: :center,
      block: tui.block(
        title: "My Ruby TUI App",
        title_alignment: :center,
        borders: [:all],
        border_color: "cyan",
        style: { fg: "white" }
      )
    )

    # 3. Use RatatuiRuby methods, too.
    tui.draw do |frame|
      frame.render_widget(view, frame.area)
    end

    # 4. Poll for events with pattern matching
    case tui.poll_event
    in { type: :key, code: "q" }
      break
    else
      # Ignore other events
    end
  end
end

How it works

  1. RatatuiRuby.run: This context manager initializes the terminal before the block starts and ensures restore_terminal is called when the block exits (even if an error occurs).

  2. Widget Shorthand: The block yields a Session object (here named tui). This object provides factory methods for every widget, allowing you to write tui.paragraph(...) instead of the more verbose RatatuiRuby::Paragraph.new(...).

  3. Method Shorthand: The session object also provides aliases for module functions of RatatuiRuby, allowing you to write tui.draw(...) instead of the more verbose RatatuiRuby.draw(...).

  4. Pattern Matching for Events: Use case...in with pattern matching for elegant event dispatch. Always include an else clause at the end to catch unmatched event types (mouse, resize, paste, focus, etc.), otherwise Ruby raises NoMatchingPatternError.

For a deeper dive into the available application architectures (Manual vs Managed), see Application Architecture.

Adding Layouts

Real-world applications often need to split the screen into multiple areas. RatatuiRuby::Layout lets you do this easily.

loop do
  tui.draw do |frame|
    # 1. Split the screen
    top, bottom = tui.layout_split(
      frame.area,
      direction: :vertical,
      constraints: [
        tui.constraint_percentage(75),
        tui.constraint_percentage(25),
      ]
    )

    # 2. Render Top Widget
    frame.render_widget(
      tui.paragraph(
        text: "Hello, Ratatui!",
        alignment: :center,
        block: tui.block(title: "Content", borders: [:all], border_color: "cyan")
      ),
      top
    )

    # 3. Render Bottom Widget with Styled Text
    # We use a Line of Spans to style specific characters
    text_line = tui.text_line(
      spans: [
        tui.text_span(content: "Press '"),
        tui.text_span(
          content: "q",
          style: tui.style(modifiers: [:bold, :underlined])
        ),
        tui.text_span(content: "' to quit."),
      ],
      alignment: :center
    )

    frame.render_widget(
      tui.paragraph(
        text: text_line,
        block: tui.block(title: "Controls", borders: [:all])
      ),
      bottom
    )
  end

  case tui.poll_event
  in { type: :key, code: "q" }
    break
  else
    # Ignore other events
  end
end

How it works

  1. tui.layout_split (RatatuiRuby::Layout.split): Takes an area (like frame.area) and splits it into multiple sub-areas based on constraints.

  2. tui.constraint_* (RatatuiRuby::Constraint): Defines how space is distributed (e.g., percentage, length, min, max).

  3. Frame#render_widget(widget, rect): You pass the specific area (like top or bottom) to render the widget into that exact region.

  4. tui.text_span (RatatuiRuby::Text::Span): Allows for rich styling within a single line of text.

Examples

These examples showcase the full power of ratatui_ruby. You can find their source code in the examples directory.

Sample Applications

Full-featured examples demonstrating complex layouts and real-world TUI patterns.

All Events

Handling terminal events is unpredictable. Developers need to know exactly what the terminal sends for Ctrl+C or a mouse drag.

This app captures and visualizes every event—keys, mouse, resize, paste, and focus.

Use it to debug your input handling or verify terminal behavior.

What you’ll learn:

all_events

Color Picker

Interactive tools require complex state. Mapping mouse clicks to widgets and handling modal dialogs creates messy code if handled in the main loop.

This app implements a full Color Picker using a “Proto-Kit (Component-Based)” pattern. Each component encapsulates its own rendering, state, and event handling.

Use it to build forms, editors, and mouse-driven tools.

What you’ll learn:

Custom Widget (Escape Hatch)

Demonstrates how to define a custom widget in pure Ruby using the render(area, buffer) escape hatch for low-level drawing.

custom_widget

Layout Split Demo

Demonstrates Layout.split with interactive attribute cycling. Features hotkey controls For direction (vertical/horizontal), all 7 flex modes (legacy, start, center, end, space_between, space_around, space_evenly), and constraint types (fill, length, percentage, min, ratio).

widget_layout_split

Login Form

Shows how to use Overlay, Center, and Cursor to build a modal login form with text input.

login_form

Widget Demos

These smaller, focused examples demonstrate specific widgets and their configuration options.