Ratatouille

Hex.pm Hexdocs.pm

Ratatouille is a declarative terminal UI kit for Elixir for building rich text-based terminal applications similar to how you write HTML.

It builds on top of the termbox API (using the Elixir bindings from ex_termbox].

For the API Reference, see: https://hexdocs.pm/ratatouille.

Toby Toby, a terminal-based Erlang observer built with Ratatouille

Getting Started

First Steps

Let's go through the bundled hello world example. To follow along, clone this repo and edit the file, or create a Mix project with ratatouille added to the deps and add code to a .exs file. Then you can run it later with mix run <file>.

In a real project, you'll probably want to use an OTP application with a proper supervision tree, but here we'll keep it as simple as possible.

First, some aliases for the modules we'll use:

alias Ratatouille.{EventManager, Window}

Next, we import the view DSL from Ratatouille.Renderer.View:

import Ratatouille.Renderer.View

Now we'll initialize the application using Ratatouille.Window. This is a gen_server that manages our terminal window and exposes a basic API for accessing information about or updating the terminal window. On load, it draws a blank canvas over the terminal:

{:ok, _pid} = Window.start_link()

In order to react to keyboard, click or resize events, we'll use Ratatouille.EventManager. The event manager allows processes to subscribe to events and then send its subscribers a message whenever an event is triggered. We need to start the event manager and subscribe the current process to any events:

{:ok, _pid} = EventManager.start_link()
:ok = EventManager.subscribe(self())

Next, we define a view. Similar to HTML, views are defined as a tree of nodes. Nodes have attributes (e.g., text: bold) and children (nested content). Every view must start with a root view element---it's sort of like the <body> tag in HTML.

hello_world_view =
  view do
    panel title: "Hello, World!", height: :fill do
      label(content: "Press 'q' to quit.")
    end
  end

Defining a view only does just that. To render it to the screen, we need to call the Window.update/1 function, passing our view as the argument.

:ok = Window.update(hello_world_view)

When a key is pressed, it'll be sent to us by the event manager. Once we receive a 'q' key press, we'll close the application. Here, we use the built-in receive function with pattern-matching in order to match only the 'q' key press event:

receive do
  {:event, %{ch: ?q}} ->
    :ok = Window.close()
end

That's it---now you can run the program with mix run <file>. To run the bundled example:

$ mix run examples/hello_world.exs

You should see the content we created and be able to quit using 'q'.

Application Loops

TODO

Writing Components

TODO

Installation

From Hex

Add Ratatouille as a dependency in your project's mix.exs:

def deps do
  [
    {:ratatouille, "~> 0.1.0"}
  ]
end

From Source

To try out the master branch, first clone the repo:

git clone https://github.com/ndreynolds/ratatouille.git
cd ratatouille

Next, fetch the deps:

mix deps.get

Finally, try out one of the included examples/:

mix run examples/rendering.exs > debug.log

If you see lots of things drawn on your terminal screen, you're good to go. Use "q" to quit in the examples.