fuelen

fuelen

Hey folks!

Want to present a toolkit for writing command-line user interfaces.

It provides a convenient interface for

  • colorizing text using tags
  • input control with validations and casting to various data types
  • select/multiselect controls, inspired by AUR package managers
  • editing text in ELIXIR_EDITOR
  • wrapping multiline colorized data into ASCII boxes
  • printing palette colors
  • progress bars, multiple bars at the same time are supported as well
  • live-updating of multiline blocks
  • working with virtual device which partially implements The Erlang I/O Protocol and doesn’t conflict with live blocks.

Here is an asciicast:
asciicast

Source code
Docs

Showing Posts 1 to 10

AndyL

AndyL

Nice!

Would it be possible to implement getch using Owl.LiveScreen (or some other method)? The idea would be to trigger on single keypress events, without using the return key.

Ruby has TTY Toolkit. Do you envision Owl to provide similar features?

I don’t understand what use-cases are addressed by the Erlang I/O protocol and Owl.LiveScreen. Can you talk a bit about that? What types of apps would be a good fit for Owl vs ExNcurses?

Owl looks great thanks very much for posting!

fuelen

fuelen OP

Would it be possible to implement getch using Owl.LiveScreen (or some other method)?

I don’t think so. At least for now, I don’t see a way how to implement this easily.

Ruby has TTY Toolkit . Do you envision Owl to provide similar features?

Some components can be definitely useful and easily ported. Didn’t know about this project, thank you. I saw GitHub - Shopify/cli-ui: CLI tooling framework with simple interactive widgets · GitHub and wanted to make something better :slight_smile: There is another similar project in JS ecosystem https://www.npmjs.com/package/inquirer. Owl doesn’t support getting char as you type, so whistles and bells like in inquirer are not possible.

I don’t understand what use-cases are addressed by the Erlang I/O protocol and Owl.LiveScreen. Can you talk a bit about that

Sure. Logger writes to stdout by default, and when you draw N progress bars, the cursor has to be moved N lines up, and replace these lines with the new content. It is okay if progress bars are rendered there, but if not, then the content will be replaced. It means that Logger has to be synchronized with an engine that is used for drawing bars. This can be achieved by simply using this line:

Logger.configure_backend(:console, device: Owl.LiveScreen)

That’s it. Now when you log something, messages will be rendered ABOVE progress bars or any other dynamic blocks. There is an example with Logger here owl/examples/emulate_compilation.exs at 9390b8f020aa6ef146c0a51af21bef5be911599b · fuelen/owl · GitHub
Let’s say you render progress bars and want to inspect my_variable using IO.inspect. Everything will be messed up if you simply type IO.inspect(my_variable). Because Owl.LiveScreen implements Erlang I/O protocol, my_variable can be easily inspected by IO.inspect(Owl.LiveScreen, my_variable, [])

What types of apps would be a good fit for Owl vs ExNcurses?

Owl doesn’t use nifs and is not for full-screen lazygit-like applications. At least for now. It is for scripts, executing which you can see the log of the program, what was selected, what was typed, etc. If you need full-screen applications, go with ExNcurses or GitHub - ndreynolds/ratatouille: A TUI (terminal UI) kit for Elixir · GitHub.

lud

lud

Great work ! I have already some projects where this can be very useful :slight_smile:

Eiji

Eiji

@fuelen Hi, I took a look at Owl.Box documentation.

I suggest to add padding, padding_x and padding_y opts:

padding opt:

"Owl" |> Owl.Box.new(padding: 5) |> to_string() |> String.trim_trailing()
# which is the same as:
"Owl" |> Owl.Box.new(padding_bottom: 5, padding_left: 5, padding_right: 5, padding_top: 5) |> to_string() |> String.trim_trailing()

padding_x opt:

"Owl" |> Owl.Box.new(padding_x: 5) |> to_string() |> String.trim_trailing()
# which is the same as:
"Owl" |> Owl.Box.new(padding_left: 5, padding_right: 5) |> to_string() |> String.trim_trailing()

padding_y opt:

"Owl" |> Owl.Box.new(padding_y: 5) |> to_string() |> String.trim_trailing()
# which is the same as:
"Owl" |> Owl.Box.new(padding_bottom: 5, padding_top: 5) |> to_string() |> String.trim_trailing()

Or even change it to keyword for example:

"Owl" |> Owl.Box.new(padding: [x: 1, y: 2]) |> to_string() |> String.trim_trailing()
# which is the same as:
"Owl" |> Owl.Box.new(padding: [bottom: 2, left: 1, right: 1, top: 2]) |> to_string() |> String.trim_trailing()

Also it would be nice to easily create a full height and/or full width boxes … So far I need to calculate it based on :io.columns() - borders - paddings.

Eiji

Eiji

@fuelen Here goes suggestions to Owl.IO:

Support for full answers and i18n:

iex> Owl.IO.confirm(answers: [false: {"n", "no"}, true: {"y", "yes"}])
Are you sure? [yN]: no
false

Owl.IO.open_in_editor should accept 2nd argument called editor for non-default editors … Think about detecting installed editors and if there are more than one editor displays select for an editor …

Optionally multiselect and select may also support answers for example:

iex> Owl.IO.select(["one", "two", "three"], answers: fn {index, _elem} -> index end)
#=> 1. one
#=> 2. two
#=> 3. three
#=>
#=> > 1
"one"

iex> Owl.IO.select(["one", "forty two", "three"], answers: fn {_index, elem} -> Macro.underscore(elem) end)
#=> one. one
#=> forty_two. forty two
#=> three. three
#=>
#=> > forty_two
"forty two"

answers here would also add ability to easily add answer left or right padding. This would also be useful for i18n (for example converting number to its equivalent in other languages).

fuelen

fuelen OP

Hey @Eiji

Good suggestions, thank you. Will add to my TODO list :slight_smile:

fuelen

fuelen OP

Hey folks!

I’m happy to announce that Owl v0.2.0 has been released :tada:

Changelog

Enhancements

  • Add Owl.Spinner widget with multiline frames support.
    asciicast
  • Support new sequences:
    • :blink_slow
    • :blink_rapid
    • :faint
    • :bright
    • :inverse
    • :underline
    • :italic
    • :overlined
    • :reverse
  • Add Owl.LiveScreen.await_render/1 function.
  • Add Owl.Data.tag/2 and Owl.Data.untag/2
  • Add padding_x, padding_y and padding options to Owl.Box.new/1
  • Calculate paddings and border size as a part of min/max width/height.
  • Align numbers in Owl.IO.select/2 and Owl.IO.multiselect by right-hand side.
  • Allow passing editor cmd to Owl.IO.open_in_editor/2 explicitly.
  • Add ability to specify primary and alternative answers in Owl.IO.confirm/2
  • Deprecate Owl.Tag.new/2. Owl.Data.tag/2 should be used instead.

Bug Fixes

  • They are present, but they are too small to be listed here
fuelen

fuelen OP

@Eiji I’ve implemented almost all your suggestions, except mapping numbers in select and multiselect. For now, I’m not sure if this is really helpful, because the whole idea is in using numbers.

Thank you once again for your feedback :beers:

Eiji

Eiji

Oh, I gave a bad example … Just think that someone’s prefers other number system like for example Arabic numbers. Also someone may want to use letters like a), b) c) etc.

Per above do you think you can add suffix option, so for example someone would set suffix as empty string (default) or ) character or something else?

fuelen

fuelen OP

Arabic numbers are already in use :smiley:
But I got the idea, will have this in my mind.

Where Next? Top

Trending in Announcing Top

type1fool
WebAuthnLiveComponent WebAuthnComponents See this post about renaming the package. Passwordless authentication for Phoenix LiveView app...
New
GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
woylie
I released Doggo, a collection of unstyled Phoenix components. https://github.com/woylie/doggo Features Unstyled Phoenix components....
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
ahamez
Hi everyone, I’ve been working on this protobuf library for 3 years. We use it in the company I work for, EasyMile, to communicate with ...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
kip
I’ll shortly be launching Text, a nascent text analysis library. Current functionality In this early version (not ready for prime time) ...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New
budgie
I love Elixir. It’s one of 2 programming languages I’ve ever fallen in love with. But I don’t use it anymore. Serverless was the promis...
New
bartblast
Hey folks, I just published a post about Hologram’s funding and where the project goes next - the short version: Curiosum as Main Spons...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews