Buy Me a Coffee at ko-fi.com
🌼

Chamomile

Build terminal apps in pure ruby.

A pure Ruby TUI framework. Core runtime, styling, and components — all in one gem.
Powers the Lazyrails TUI.

gem install chamomile
counter.rb
require "chamomile"

class Counter
  include Chamomile::Application

  def initialize
    @count   = 0
    @seconds = 0
  end

  on_key(:up, "k")   { @count += 1 }
  on_key(:down, "j") { @count -= 1 }
  on_key("r")        { @count = 0 }
  on_key("q")        { quit }

  on_tick { @seconds += 1; tick(1.0) }

  def on_start
    tick(1.0)
  end

  def view
    vertical(align: :left) do
      text "Counter", bold: true, color: "#7d56f4"
      text ""
      text "Count:   #{@count}"
      text "Uptime:  #{@seconds}s"
      text ""
      status_bar "up/k increment | down/j decrement | r reset | q quit"
    end
  end
end

Chamomile.run(Counter.new)

How It Works

Event-driven architecture, the Ruby way.

1

Application

Your class is the app. Include Chamomile::Application and define view.

2

Events

Declare callbacks with on_key, on_mouse, on_tick, on_resize. Or override update for full control.

3

View

Returns a string. The renderer diffs it and only redraws what changed.

4

Commands

Lambdas on a thread pool. They return events that feed back into your callbacks.

Batteries Included

Everything is a command. Async by default, composable by design.

timers & ticks
def on_start
  tick(1.0)  # fire a TickEvent after 1s
end

on_tick {
  @seconds += 1
  tick(1.0)  # keep ticking
}
shell & exec
# Run a command, get typed results
shell("git status", envelope: :git)

# Suspend the TUI, run an editor
exec("vim", "file.rb")

# Handle the result in update
when Chamomile::ShellResult
  if msg.envelope == :git
    @output = msg.stdout
  end
cancel & stream
# Cooperative cancellation
token, cmd = cancellable { |t|
  until t.cancelled?
    do_work
  end
}
cancel(token)

# Streaming — emit many messages
token, cmd = stream { |push, t|
  lines.each { |l| push.(l) }
}

Components

13 ready-made components, built in. Same handle/view protocol.

TextInput Single-line input with cursor, word editing, echo modes, paste
TextArea Multi-line editor with line numbers, 2D cursor, page nav
Viewport Scrollable content pane with keyboard and mouse navigation
Table Scrollable data table with column definitions and focus
List Filterable list with fuzzy search, delegates, pagination
FilePicker Async directory browser with extension filtering
Spinner 12 animation types — dots, lines, moon, and more
Progress Spring-animated progress bar with gradient colors
Timer Countdown with timeout notification
Stopwatch Count-up timer with start/stop/reset
Paginator Dot or arabic page navigation
Cursor Blink/static/hide modes with focus support
Help Short/full help view from key binding definitions