OTP-native terminal UI for Elixir.
Cringe is an experiment in building interactive terminal apps with declarative layouts, supervised runtimes, semantic input events, render-only widgets, and ExUnit-friendly render assertions. The name is a joke; the goal is serious terminal UI ergonomics for the BEAM.
Status
Early alpha. The API is not stable yet, but Cringe is usable for small interactive terminal experiments.
First document
use Cringe
box padding: 1 do
column gap: 1 do
text("Cringe", color: :green, bold: true)
text("Terminal UI for the BEAM")
end
end
|> render(width: 80, ansi: true)
|> IO.puts()Render-only widgets
use Cringe
column gap: 1 do
spinner(frame: 2, label: "Loading")
progress(value: 0.42, width: 16, label: "Build")
input(value: "cringe", focused: true, width: 24)
select(options: ["Dashboard", "Logs", "Settings"], selected: 1)
endInteractive app
defmodule Counter do
use Cringe.App
def init(_opts), do: {:ok, %{count: 0}}
def handle_event(%Cringe.Event.Key{key: :up}, state), do: {:noreply, %{state | count: state.count + 1}}
def render(state), do: box(text("Count: #{state.count}"), padding: 1)
end
{:ok, app} = Cringe.run(Counter, backend: Cringe.Runtime.Backend.Terminal)
Cringe.Runtime.dispatch(app, Cringe.Event.key(:up))
Cringe.Runtime.paint(app)Run examples locally:
mix run examples/hello.exs
mix run examples/dashboard.exs
mix run examples/layout.exs
mix run examples/dsl.exs
mix run examples/widgets.exs
mix run examples/counter.exs
mix run examples/interactive_counter.exs
mix run examples/interactive_input.exs
mix run examples/form.exs
Installation
Once published, add cringe to your dependencies:
def deps do
[
{:cringe, "~> 0.2"}
]
endDocumentation is published at https://hexdocs.pm/cringe.
Benchmarks
Cringe includes local Benchee benchmarks for render, canvas, painter, and input paths:
mix bench
Benchmarks are for local regression checks and are not part of CI.