Coder Social home page Coder Social logo

owl's People

Contributors

fuelen avatar kianmeng avatar moogle19 avatar zachallaun avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

owl's Issues

Proposal: Add `Owl.Data.from_ansidata/1` - mechanism for dealing with existing escape codes

Context

One of the really awesome things that owl brings to the table is the notion of tags. They're a great primitive that dramatically simplify a lot of things.

An issue I've run up against, however, is that some things break down when you are dealing with iodata that already include ANSI escape sequences. For instance, formatting and syntax-highlighting code using Inspect.Algebra.to_doc/2 and Inspect.Algebra.format/2 does not provide a mechanism other than applying the escape sequences from IO.ANSI. When this iodata is then used with things like Owl.Box or Owl.Table, things break because length calculations for each line assume that data is formatted using tags.

The first time I ran into this, I was able to submit a PR to the library that allowed me to use Owl.Data.tag/2 to colorize, but I think it's pretty well outside the scope of Elixir core to expect a similar option there. πŸ™‚

Proposal

Add Owl.Data.from_ansidata/1 to convert existing formatted iodata to Owl.Data.t().

I would personally be fine with the constraint that the iodata should be "well formatted" in that escape sequences should be separate binaries in a list, not concatenated strings. For example:

iex> ansidata = IO.ANSI.format([:red, "foo"])
[[[[] | "\e[31m"], "foo"], | "\e[0m"]

iex> Owl.Data.from_ansidata(ansidata)
[#Owl.Tag[:red]<"foo">]

iex> Owl.Data.from_ansidata("\e[31mfoo\e[0m")
["\e[31mfoo\e[0m"]

Portable getch?

I’ve been unable to find a simple & portable getch implementation for Elixir. The use case is to detect single keypress events, without needing the return key.

Comparable tools like inquirer/npm and tty/ruby use getch to build features like type-ahead, pull-down menus, etc.

To implement getch I've looked at ex_ncurses, rustler, System.cmd, Ports, etc. This is the best I’ve come up with so far:

#!/usr/bin/env elixir

ruby_cmd = ~S"""
  ruby -e '
    require "io/console"
    @output = IO.new(4)
    @output.sync = true
    char = STDIN.getch
    @output.write(char)
  '
"""

IO.write("Press a key > ")
port = Port.open({:spawn, ruby_cmd}, [:binary, :nouse_stdio]) 
receive do
  {^port, {:data, result}} -> IO.puts("\nChar: #{result}")
end

This works on my Linux dev machine, but depends on Ruby.

Does anyone know of a portable getch implementation? Maybe with Rustler or precompiled binaries or ???

Consecutive IO.puts of zipped data results in display glitches

I'm trying to print a grid of outputs (think: sudoku) without double-wrapping them into boxes.

I expected this to work:

[
  Owl.Data.zip(Owl.Box.new("a b\nc d"), Owl.Box.new("a b\nc d")),
  Owl.Data.zip(Owl.Box.new("a b\nc d"), Owl.Box.new("a b\nc d"))
]
|> Owl.IO.puts()

But it displays as something that doesn't paste well here, but the important point is that the last line of the first zip and the first line of the second appear contiguously.

β””β”€β”€β”€β”˜β””β”€β”€β”€β”˜β”Œβ”€β”€β”€β”β”Œβ”€β”€β”€β”

Really don't want to do a nested box and the only workaround I can find is unrolling my |> and sticking an extra linefeed between the lines.

Proposal: Add `Owl.Data.flex_row` layout primitive

Owl.Table is a great tool for making fancy tables, but is a bit too rigid to be used as a general-purpose layout primitive. I'd love some kind of flex-row option that allows layout out multiple data side-by-side, wrapping them down if the terminal width is not sufficient.

Examples:

# Show a side-by-side diff if the terminal width is sufficient. Expand will pad the data so they are the same length.
Owl.Data.flex_row([left_diff, right_diff], expand: true)

# Lay out multiple boxes side-by-side.
Owl.Data.flex_row([box1, box2, box3])

# Lay out multiple boxes side-by-side, wrap all down if terminal width is insufficient
Owl.Data.flex_row([box1, box2, box3], wrap_all: true)

I'd imagine a padding_x and padding_y could also be useful, but perhaps gap_x and gap_y would be better in this particular case.

Proposal: ProgressBar (and other widgets) can be attached to a block

My use case is that many spinners are showing the progress of various subtasks, and a "grand total" progress bar shows the overall progress through the full job. I would like the progress bar to stick to the bottom of the screen so that it remains visible, no matter how many subtasks are active.

To do this, it would be ideal if I could create a LiveScreen.add_block and then attach the progress bar to it during .start .

Flapping display when spinners scroll off-screen

I'm running a concurrent job and each thread has its own progress spinner. What I've found is that the spinners display correctly as long as they're all in the visible terminal area, but once they scroll off-screen then the spinners begin to compete with one another for updates. Instead of updating in-place, each spinner is repeatedly written at the bottom of output as a new line.

Here's a minimal example (terminal must be < 100 rows tall):

1..100
|> Enum.each(fn x ->
  Task.async(fn ->
    Owl.Spinner.run(
      fn ->
        Process.sleep(5_000)
        :ok
      end,
      labels: [
        ok: "Completed: #{x}",
        processing: "Processing: #{x}",
      ]
    )
  end)
end)

Process.sleep(5_000)

Thank you for the great library!

Copy/pastable inspect protocol for tags

Latest versions of Elixir have moved to a copy-pastable Inspect convention. E.g. from MapSet:

iex> MapSet.new([1, :two, {"three"}])
MapSet.new([1, :two, {"three"}])

Tags could do the same:

iex> Owl.Data.tag(β€œhello”, :red)
Owl.Data.tag(β€œhello”, :red)

Happy to implement. :)

Silencing progress bar during tests?

Hey, thanks for this library I love building CLI apps with it!

I have written some tests with capture_io but I can't capture the progress bar output (I'd just like to silence it).
What would be the best way?

the border will appear misaligned when emoji was used in the title

image

iex(1)> "Owl" |> Owl.Box.new(title: "hello") |> Owl.IO.puts()
β”Œβ”€hello────┐
β”‚Owl       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
:ok
iex(2)> "Owl" |> Owl.Box.new(title: "πŸ˜‚ hello") |> Owl.IO.puts()
β”Œβ”€πŸ˜‚ hello────┐
β”‚Owl         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
:ok

Proposal: add word wrapping

If I try to use a string that is too long, I see naive wrapping.

"Very Long Line" |> Owl.Box.new(max_width: 4, border_style: :none)
Very
 Lon
g Li
ne  

It would be nice if this wrapped to

Very
Long
Line

Any pointers on where to start adding this, and how feasible it might be?

`Owl.Box` collapses newlines

Hi there! First off, thanks very much for making such a useful library. I've been working on a library with a CLI and Owl has been a really big improvement.

I noticed what I assume is a bug in Owl.Box where consecutive newlines are collapsed to a single newline. I admit that I haven't done further testing to know whether this is actually an issue with some other primitive that Owl.Box is using, or whether it's specific to the box implementation.

iex(1)> "foo\nbar" |> Owl.Box.new() |> Owl.IO.puts()
β”Œβ”€β”€β”€β”
β”‚fooβ”‚
β”‚barβ”‚
β””β”€β”€β”€β”˜
:ok

iex(2)> "foo\n\nbar" |> Owl.Box.new() |> Owl.IO.puts()
β”Œβ”€β”€β”€β”
β”‚fooβ”‚
β”‚barβ”‚
β””β”€β”€β”€β”˜
:ok

iex(3)> "foo\n\n\nbar" |> Owl.Box.new() |> Owl.IO.puts()
β”Œβ”€β”€β”€β”
β”‚fooβ”‚
β”‚barβ”‚
β””β”€β”€β”€β”˜
:ok

iex(4)> "foo\n \n\nbar" |> Owl.Box.new() |> Owl.IO.puts()
β”Œβ”€β”€β”€β”
β”‚fooβ”‚
β”‚   β”‚
β”‚barβ”‚
β””β”€β”€β”€β”˜
:ok

Adding additional newline characters does not affect the formatting, unless a space is added (as on iex(4)), in which case the line with the space will be respected.

This makes it somewhat difficult to insert preformatted text. If you are inserting a single preformatted string, you can get around this by splitting on newlines, mapping over the result and replacing every "" with " " before re-joining on newlines, but this is bit of non-obvious extra work. =)

Owl.LiveScreen issues on Elixir >= 1.15 ?

I tried the compiler example with both Terminal.app and ITerm2 and it just intermingles the content, but the same code works perfectly on Linux - keeping the outputs separate. Other examples all seem to work fine. Is this expected?
Screenshot 2024-03-15 at 10 30 46

`Owl.LiveScreen` terminal input (proof of concept)

Since it's unclear how far we are from a portable getch, I thought I'd experiment with some way of getting user input while using Owl.LiveScreen.

After some hacking around, I have a proof of concept that I wanted to share:

live_input_proof_of_concept

The basic idea is to allow Owl.LiveScreen to render a singleton prompt, and then use \e7 (cursor position save) and \e8 (cursor position restore) to maintain the cursor position during renders. The changes I made in the linked branch are very rough -- basically just getting something working. There are a number of open questions:

  • How reliable is \e7 and \e8? It seems like most modern terminals support them, but they're non-standard, I believe. There's also \e[s and \e[u -- there are some notes about them in this gist.
  • What is the desired API? Right now I've just added an :on_input option to add_block that can be a function taking the input and block state and returning a new state.
  • A limitation with the current API that I ran up against: LiveScreen.update replaces the whole state and the owl demo used Stream.iterate to move the owl, but I wanted the color to react to the user input. For this proof of concept, I just broke the API and decided that LiveScreen.update would merge the maps so that the owl color wouldn't be overriden by every update from the stream iteration. It may be desirable for LiveScreen.update to take an update function instead that can be applied to the current state of the block.

Colour output on different terminals

Weird one here... I'm slightly abusing Owl to render an interface for a MUD that is displayed over a LiveView, and I'm hitting a funny issue when deploying.

The ansi colour codes work fine on dev, when I'm usind a LiveScreen and updating a block, I observe its io_request message and the colour codes are present.

But when I do this in prod (Debian / Fly.io), the same messages contain no colour codes. I'm observing the raw data in logs:

dev

οΏ½[2KοΏ½[33HelloοΏ½[39m world

prod

οΏ½[2KHello world

(No colour codes)

Any pointers for what might be happening? I've been trying to debug this morning and my best guess is that Owl is doing some kind of terminal support check and disabling colours...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.