Project

thaum

0.0
The project is in a healthy, maintained state
Build full-screen terminal apps in Ruby. Thaum provides a declarative layout DSL, composable sigils (widgets) and octagrams (composite components), a diff-based renderer with truecolor support, event dispatch with ticks and background actions, focus management, themes, and snapshot testing.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

~> 1.0

Runtime

 Project Readme
╭─────────────────────────────────────────────────────╮
│  ▒▒▒▒▒▒▒▒╗ ▒▒╗  ▒▒╗  ▒▒▒▒▒╗  ▒▒╗   ▒▒╗ ▒▒▒╗   ▒▒▒╗  │ 
│  ╚══▒▒╔══╝ ▒▒║  ▒▒║ ▒▒╔══▒▒╗ ▒▒║   ▒▒║ ▒▒▒▒╗ ▒▒▒▒║  │
│     ▒▒║    ▒▒▒▒▒▒▒║ ▒▒▒▒▒▒▒║ ▒▒║   ▒▒║ ▒▒╔▒▒▒▒╔▒▒║  │
│     ▒▒║    ▒▒╔══▒▒║ ▒▒╔══▒▒║ ▒▒║   ▒▒║ ▒▒║╚▒▒╔╝▒▒║  │
│     ▒▒║    ▒▒║  ▒▒║ ▒▒║  ▒▒║ ╚▒▒▒▒▒▒╔╝ ▒▒║ ╚═╝ ▒▒║  │
│     ╚═╝    ╚═╝  ╚═╝ ╚═╝  ╚═╝  ╚═════╝  ╚═╝     ╚═╝  │ 
╰─────────────────────────────────────────────────────╯

A Thaum is the basic unit of magical strength. It has been universally established as the amount of magic needed to create one small white pigeon or three normal-sized billiard balls. *

— Terry Pratchett

Compose full-screen TUI apps from a few parts:

  • App — top-level container that owns layout, focus, and event handling
  • Layout — vertical/horizontal split DSL (region(height: 3), region(width: :fill), …)
  • Sigil — focusable, renderable leaf component (Text, TextInput, Select, Button, ScrollView, Table, Spinner, ProgressBar, Checkbox, Tabs)
  • Octagram — composite that contains sigils and presents itself as a distributable component

Features:

  • Buffer-diffing renderer with synchronized output (\e[?2026h)
  • Truecolor with automatic degradation to 256 / 16 / no-color based on $COLORTERM and $TERM
  • Themes (8 built-in palettes — Solarized, Gruvbox, Catppuccin, …)
  • Event dispatch: keys, paste, ticks, resize, suspend/resume
  • Focus with Tab/Shift-Tab cycling and overridable focus_order
  • Background work via Thaum::Action (concurrent-ruby thread pool)
  • Box-drawing junction merging — adjacent borders resolve to the correct corner/tee glyph automatically, across light/heavy/double weights
  • Snapshot testing via thaum/minitest

Installation

bundle add thaum

Or:

gem install thaum

Requires Ruby 3.2 or newer.

Usage

The Hello World example:

require "thaum"

class HelloWorldApp
  include Thaum::App

  def on_key(event)
    quit if event.key == :escape
  end

  def initialize
    @greeting = Thaum::Text.new(content: "Hello World!", align: :center)
  end

  def partition
    vertical do
      region(height: :fill) { @greeting }
    end
  end
end

Thaum.run(HelloWorldApp.new)

Examples

The examples/ directory has a runnable demo for every shipped sigil plus a few composed apps:

bundle exec ruby examples/counter.rb         # minimal app
bundle exec ruby examples/picker.rb          # filter-as-you-type list selector
bundle exec ruby examples/todo.rb            # TextInput + Select + Button
bundle exec ruby examples/stopwatch.rb       # on_tick driven
bundle exec ruby examples/theme_picker.rb    # cycle the built-in themes
bundle exec ruby examples/layout_demo.rb     # nested Layout DSL + border junctions
bundle exec ruby examples/octagram_picker.rb # picker packaged as an Octagram

Per-sigil demos: text.rb, select.rb, checkbox.rb, tabs.rb, spinner.rb, progress_bar.rb, scroll_view.rb, table.rb.

Snapshot testing

require "thaum/minitest"

class MySigilTest < Minitest::Test
  def test_renders_greeting
    buffer = Thaum::Rendering::Buffer.new(width: 20, height: 3)
    canvas = Thaum::Rendering::Canvas.new(buffer: buffer, rect: Thaum::Rect.new(x: 0, y: 0, width: 20, height: 3))
    MySigil.new.render(canvas: canvas, theme: Thaum::Themes::DEFAULT)
    assert_snapshot(buffer, "my_sigil/greeting")
  end
end

First run writes test/snapshots/my_sigil/greeting.txt (or .ans if the output contains ANSI styling) and passes with a warning. Re-run with UPDATE_SNAPSHOTS=1 to refresh existing snapshots.

Development

bin/setup       # bundle install
rake test       # run the test suite (Minitest)
rake rubocop    # lint
rake            # both
bin/console     # IRB with thaum required

To install locally: bundle exec rake install. To cut a release: bump lib/thaum/version.rb, then bundle exec rake release.

Contributing

Bug reports and pull requests are welcome at https://github.com/urug/thaum.

License

MIT. See LICENSE.txt.

Footnotes

* Also, a terminal window