https://github.com/goose97/orange
https://github.com/goose97/orange
Last synced: about 1 year ago
JSON representation
- Host: GitHub
- URL: https://github.com/goose97/orange
- Owner: Goose97
- Created: 2024-03-15T13:55:13.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2025-04-11T14:28:08.000Z (about 1 year ago)
- Last Synced: 2025-04-11T15:42:01.147Z (about 1 year ago)
- Language: Elixir
- Size: 684 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
Awesome Lists containing this project
README
[](https://hex.pm/packages/orange) [](https://hexdocs.pm/orange)
Orange is a framework to build TUI (terminal UI) applications in Elixir. Its high-level features are:
- A DSL to describe UI component. The syntax is inspired by React. For example, an snippet like this:
```elixir
rect style: [border: true, padding: {0, 1}, height: 10, width: 20] do
rect style: [color: :red] do
"Hello"
end
rect do
"World"
end
end
```
will render this:

- Support handling terminal events: currently, only keyboard events are supported.
- Support custom components: you can create component from builtin primitives. Custom components can encapsulate state and logic.
- A collection of UI components: Input, VerticalScrollRect, ...
## Important
When using Orange, it is essential that you prevent the Erlang VM from reading stdin as it can interfere with the terminal events handling logic. You can achieve this via the `-noinput` flag:
```sh
elixir --erl "-noinput" -S mix run --no-halt
```
## Examples
First, we need to create a root component:
```elixir
defmodule Counter.App do
@behaviour Orange.Component
import Orange.Macro
@impl true
# Each component can have an internal state
# Also, a component can subscribe to receive terminal events
def init(_attrs), do: %{state: %{count: 0}, events_subscription: true}
@impl true
def handle_event(event, state, _attrs) do
case event do
# Arrow up to increase counter
%Orange.Terminal.KeyEvent{code: :up} ->
{:update, %{state | count: state.count + 1}}
# Arrow down to decrease counter
%Orange.Terminal.KeyEvent{code: :down} ->
{:update, %{state | count: state.count - 1}}
%Orange.Terminal.KeyEvent{code: {:char, "q"}} ->
# Quit the application
Orange.stop()
:noop
_ ->
:noop
end
end
@impl true
def render(state, _attrs, _update) do
rect style: [border: true, padding: 1] do
"Counter: #{state.count}"
end
end
end
```
Then start the application:
```elixir
Orange.start(Counter.App)
```
For more examples, see [here](/examples).