Giter Site home page Giter Site logo

piotrmurach / tty-box Goto Github PK

View Code? Open in Web Editor NEW
180.0 4.0 16.0 213 KB

Draw various frames and boxes in your terminal window

Home Page: http://ttytoolkit.org

License: MIT License

Ruby 99.76% Shell 0.24%
rubygem ruby terminal console box-drawing terminal-graphics boxplot frame border tty

tty-box's Introduction

TTY Toolkit logo

TTY::Box Gitter

Gem Version Actions CI Build status Maintainability Coverage Status Inline docs

Draw various frames and boxes in the terminal window.

TTY::Box provides box drawing component for TTY toolkit.

Box drawing

Installation

Add this line to your application's Gemfile:

gem "tty-box"

And then execute:

$ bundle

Or install it yourself as:

$ gem install tty-box

Contents

1. Usage

Using the frame method, you can draw a box in a terminal emulator:

box = TTY::Box.frame "Drawing a box in", "terminal emulator", padding: 3, align: :center

And then print:

print box
# =>
# ┌───────────────────────┐
# │                       │
# │                       │
# │                       │
# │   Drawing a box in    │
# │   terminal emulator   │
# │                       │
# │                       │
# │                       │
# └───────────────────────┘

2. Interface

2.1 frame

You can draw a box in your terminal window by using the frame method and passing a content to display. By default the box will be drawn around the content.

print TTY::Box.frame "Hello world!"
# =>
# ┌────────────┐
# │Hello world!│
# └────────────┘

You can also provide multi line content as separate arguments.

print TTY::Box.frame "Hello", "world!"
# =>
# ┌──────┐
# │Hello │
# │world!│
# └──────┘

Alternatively, provide a multi line content using newline chars in a single argument:

print TTY::Box.frame "Hello\nworld!"
# =>
# ┌──────┐
# │Hello │
# │world!│
# └──────┘

Finally, you can use a block to specify content:

print TTY::Box.frame { "Hello world!" }
# =>
# ┌────────────┐
# │Hello world!│
# └────────────┘

You can also enforce a given box size without any content and use tty-cursor to position content whatever you like.

box = TTY::Box.frame(width: 30, height: 10)

When printed will produce the following output in your terminal:

print box
# =>
# ┌────────────────────────────┐
# │                            │
# │                            │
# │                            │
# │                            │
# │                            │
# │                            │
# │                            │
# │                            │
# └────────────────────────────┘

Alternatively, you can also pass a block to provide a content for the box:

box = TTY::Box.frame(width: 30, height: 10) do
  "Drawin a box in terminal emulator"
end

When printed will produce the following output in your terminal:

print box
# =>
# ┌────────────────────────────┐
# │Drawing a box in terminal   │
# │emulator                    │
# │                            │
# │                            │
# │                            │
# │                            │
# │                            │
# │                            │
# └────────────────────────────┘

2.2 position

By default, a box will not be positioned. To position your box absolutely within a terminal window use :top and :left keyword arguments:

TTY::Box.frame(top: 5, left: 10)

This will place box 10 columns to the right and 5 lines down counting from the top left corner.

If you wish to center your box within the terminal window then consider using tty-screen for gathering terminal screen size information.

2.3 dimension

At the very minimum a box requires to be given size by using two keyword arguments :width and :height:

TTY::Box.frame(width: 30, height: 10)

If you wish to create a box that depends on the terminal window size then consider using tty-screen for gathering terminal screen size information.

For example to print a box that spans the whole terminal window do:

TTY::Box.frame(width: TTY::Screen.width, height: TTY::Screen.height)

2.4 title

You can specify titles using the :title keyword and a hash value that contains one of the :top_left, :top_center, :top_right, :bottom_left, :bottom_center, :bottom_right keys and actual title as value. For example, to add titles to top left and bottom right of the frame do:

box = TTY::Box.frame(width: 30, height: 10, title: {top_left: "TITLE", bottom_right: "v1.0"})

which when printed in console will render the following:

print box
# =>
# ┌TITLE───────────────────────┐
# │                            │
# │                            │
# │                            │
# │                            │
# │                            │
# │                            │
# │                            │
# │                            │
# └──────────────────────(v1.0)┘

2.5 border

There are three types of border :ascii, :light, :thick. By default the :light border is used. This can be changed using the :border keyword:

box = TTY::Box.frame(width: 30, height: 10, border: :thick)

and printing the box out to console will produce:

print box
# =>
# ╔════════════════════════════╗
# ║                            ║
# ║                            ║
# ║                            ║
# ║                            ║
# ║                            ║
# ║                            ║
# ║                            ║
# ║                            ║
# ╚════════════════════════════╝

You can also selectively specify and turn off border parts by passing a hash with a :border key. The border parts are:

                 :top
    :top_left ┌────────┐ :top_right
              │        │
        :left │        │ :right
              │        │
 :bottom_left └────────┘ :bottom_right
               :bottom

The following are available border parts values:

Border values ASCII Unicode Light Unicode Thick
:line -
:pipe | \│ \║
:cross +
:divider_up +
:divider_down +
:divider_left +
:divider_right +
:corner_top_left +
:corner_top_right +
:corner_bottom_left +
:corner_bottom_right +

For example, to change all box corners to be a :cross do:

box = TTY::Box.frame(
  width: 10, height: 4,
  border: {
    top_left: :cross,
    top_right: :cross,
    bottom_left: :cross,
    bottom_right: :cross
  }
)
print box
# =>
# ┼────────┼
# │        │
# │        │
# ┼────────┼

If you want to remove a given border element as a value use false. For example to remove bottom border do:

TTY::Box.frame(
  width: 30, height: 10,
  border: {
    type: :thick,
    bottom: false
  })

2.6 styling

By default drawing a box doesn't apply any styling. You can change this using the :style keyword with foreground :fg and background :bg keys for both the main content and the border:

style: {
  fg: :bright_yellow,
  bg: :blue,
  border: {
    fg: :bright_yellow,
    bg: :blue
  }
}

The above style configuration will produce the result similar to the top demo, a MS-DOS look & feel window.

You can disable or force output styling regardless of the terminal using the enable_color keyword. By default, the color support is automatically detected.

TTY::Box.frame({
  enable_color: true, # force to always color output
  style: {
    border: {
      fg: :bright_yellow,
      bg: :blue
    }
  }
})

2.7 formatting

You can use :align keyword to format content either to be :left, :center or :right aligned:

box = TTY::Box.frame(width: 30, height: 10, align: :center) do
  "Drawing a box in terminal emulator"
end

The above will create the following output in your terminal:

print box
# =>
# ┌────────────────────────────┐
# │ Drawing a box in terminal  │
# │          emulator          │
# │                            │
# │                            │
# │                            │
# │                            │
# │                            │
# │                            │
# └────────────────────────────┘

You can also use :padding keyword to further format the content using the following values:

[1,3,1,3]  # => pad content left & right with 3 spaces and add 1 line above & below
[1,3]      # => pad content left & right with 3 spaces and add 1 line above & below
1          # => shorthand for [1,1,1,1]

For example, if you wish to pad content all around do:

box = TTY::Box.frame(width: 30, height: 10, align: :center, padding: 3) do
  "Drawing a box in terminal emulator"
end

Here's an example output:

print box
# =>
# ┌────────────────────────────┐
# │                            │
# │                            │
# │                            │
# │     Drawing a box in       │
# │     terminal emulator      │
# │                            │
# │                            │
# │                            │
# └────────────────────────────┘
#

2.8 messages

Box messages

2.8.1 info

To draw an information type box around your content use info:

box = TTY::Box.info("Deploying application")

And then print:

print box
# =>
# ╔ ℹ INFO ═══════════════╗
# ║                       ║
# ║ Deploying application ║
# ║                       ║
# ╚═══════════════════════╝

2.8.2 warn

To draw a warning type box around your content use warn:

box = TTY::Box.warn("Deploying application")

And then print:

print box
# =>
# ╔ ⚠ WARNING ════════════╗
# ║                       ║
# ║ Deploying application ║
# ║                       ║
# ╚═══════════════════════╝

2.8.3 success

To draw a success type box around your content use success:

box = TTY::Box.success("Deploying application")

And then print:

print box
# =>
# ╔ ✔ OK ═════════════════╗
# ║                       ║
# ║ Deploying application ║
# ║                       ║
# ╚═══════════════════════╝

2.8.4 error

To draw an error type box around your content use error:

box = TTY::Box.error("Deploying application")

And then print:

print box
# =>
# ╔ ⨯ ERROR ══════════════╗
# ║                       ║
# ║ Deploying application ║
# ║                       ║
# ╚═══════════════════════╝

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/piotrmurach/tty-box. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the TTY::Box project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.

Copyright

Copyright (c) 2018 Piotr Murach. See LICENSE for further details.

tty-box's People

Contributors

coquizen avatar danielvartanov avatar lainlayer avatar piotrmurach avatar sevenmaxis 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

tty-box's Issues

Suggestion: Terminal UI

What are the chances of having the ability to create terminal UIs in TTY::Box (or somewhere else in the TTY toolkit)?

I think with what TTY brings to the table, having a minimum viable solution - one that allows creating panels that can a) be refreshed on interval and b) always contain their own content - is not too far out of reach, is it?

So we can do UIs like this, without relying on the complex ncurses related projects?

image

Ideally (but I know highly unlikely), I wish Ruby would have something similar to Go's termui:

image

Empty lines are deleted under certain circumstances

Describe the problem

When using text with empty lines, in some cases the empty lines are removed.
I observed this when trying to show the contents of a file inside a TTY::Box.

Steps to reproduce the problem

require 'tty-box'

def draw(text)
  options = { width: 30, height: 10 }
  puts TTY::Box.frame(options) { text }
end

system 'clear'
arr = ["hello\n", "\n", "world\n", "\n"]

draw (arr * 9).join
draw (arr * 2).join

Actual behaviour

┌────────────────────────────┐
│hello                       │
│world                       │
│hello                       │
│world                       │
│hello                       │
│world                       │
│hello                       │
│world                       │
└────────────────────────────┘
┌────────────────────────────┐
│hello                       │
│                            │
│world                       │
│                            │
│hello                       │
│                            │
│world                       │
│                            │
└────────────────────────────┘

Expected behaviour

Expected both outputs to be like the second one

Describe your environment

  • OS version: Ubuntu 18.04.3
  • Ruby version: ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]
  • TTY::Box version: tty-box (0.5.0)

Block content not rendering

Version 0.5.0, ruby 2.6.3p62, Mac, iTerm

1] pry(main)> print TTY::Box.frame "Hello world!"
┌────────────┐
│Hello world!│
└────────────┘
=> nil
[2] pry(main)> print TTY::Box.frame do
[2] pry(main)*   "Hello world!"
[2] pry(main)* end
┌─┐
└─┘
=> nil

tty-box and tty-prompt will not work together

I am trying to use tty-box but my app already uses tty-prompt. The are both requiring tty-cursor but require different versions. Is it possible to use both box and prompt?

environment

  • Ruby version:2.7.0
  • TTY::Box version:0.5

Not compatible with TTY::Link

Great gem! I'm just having some small issues with TTY::Link:

require "tty/box"
require "tty/link"

link = TTY::Link.link_to("Link", "http://example.com")

box1 = TTY::Box.frame(title: { top_left: link }) do
  "<content>"
end

box2 = TTY::Box.frame(title: { top_left: "No link" }) do
  "<content>"
end

puts box1
puts box2

puts TTY::Box::VERSION
puts TTY::Link::VERSION

image

Color support?

I am playing around with TTY::Box right now.

I tried to add colour output:

box = TTY::Box.frame(width: 70, height: 15, border: :thick,  color: :steelblue)

:steelblue should refer to the HTML colour steelblue, and the RGB values that
this represents. TTY::Box does not seem to accept a :color keyword.

Could this be added? KDE konsole has no problem with RGB colors. In one
of my color-related projects I make heavy use of colours, so colored
borders and colored text content of the box would be useful IMO.

Broken box when using a certain combination of size and padding

Describe the problem

There seems to be a problem with a certain combinations of width, content length and padding.

Steps to reproduce the problem

Using the very first code from the README, only with padding: 1 instead of 3:

require 'tty-box'

box = TTY::Box.frame(
  width: 30,
  height: 10,
  align: :center,
  padding: 1
) do
  "Drawing a box in terminal emulator"
end

puts box

Actual behavior

The script produces this output:

$ ruby test.rb
┌────────────────────────────┐
│                            │
│ Drawing a box in terminal  │
│ emulator │
│          emulator          │
│                            │
│                            │
│                            │
│                            │
└────────────────────────────┘

Provided also as a screenshot:

image

Describe your environment

  • OS version: Ubuntu 16
  • Ruby version: 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]
  • TTY::Box version: 0.2.0

box is printed in the top of terminal

I use default demo code to test

require 'tty-box'

box = TTY::Box.frame(
  width: 30,
  height: 10,
  align: :center,
  padding: 3
) do
  "Drawing a box in terminal emulator"
end

print box

I'v been trying to run that in irbor by ruby file.rb command execution.
Result is always the same: code ignores current line in terminal, override previous history and prints box in the top of the console window

environment:
ruby version is 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]
iTem version is 3.1.7

Suggestion: Providing a way to generate connectable boxes

I see there is some preparation in the code for borders other than the four corners:

tty-box/lib/tty/box.rb

Lines 14 to 17 in 94ebd86

BOX_CHARS = {
light: %w[ ],
thick: %w[ ]
}.freeze

But I could not find anything in the code to let me use these cross connectors and dividers.

Is there currently a way to draw boxes that connect nicely to each other, like this:

╔════════╦════════╗
║left    ║right   ║
╚════════╩════════╝

Instead of like this:

╔════════╔════════╗
║left    ║right   ║
╚════════╚════════╝

If there is none, I would like to cast my "+1" for having it.

I am sure you have already thought about it, but just in case, I would like to suggest one of two interfaces for the user to specify these borders:

Option 1: More intuitive, less flexible, probably more work

By specifying only the side of the box that should be connected, like:

puts TTY::Box.frame(top: 0, left: 9, width: 10, height: 3. connect: :left) { "right" }

will produce:

┬────────┐
│right   │
┴────────┘

and connect: [:left, :top] will produce:

┼────────┤
│right   │
┴────────┘

Option 2: Less intuitive, more flexible, probably less work

Instead of using the "magic" connect, maybe let the user specify explicitly which corners they need:

options = {
  top: 0, left: 9, width: 10, height: 3,
  corner1: :cross,
  corner4: :left_divider
}

# corners are indexed clockwise, starting from top left
# values can use the same names as the `*_char` methods

puts TTY::Box.frame(options) { "right" }

`*': negative argument

Describe the problem

With a certain body the box fails to draw.

Steps to reproduce the problem

        box = TTY::Box.frame(
          width: TTY::Screen.width,
          height: mr.description.lines.count + 10,
          padding: 1,
          title: { top_center: mr.title[0..TTY::Screen.width - 3],
                   bottom_left: "State: #{mr.state}",
                   bottom_right: "by #{mr.author.name}" }
        ) do
          mr.description # content is "Closes #360\r\n\r\nCloses !217 "
        end
        puts box

Actual behaviour

Traceback (most recent call last):
        16: from /usr/local/bin/gl:23:in `<main>'
	15: from /usr/local/bin/gl:23:in `load'
	14: from /var/lib/gems/2.5.0/gems/gl-0.3.1/exe/gl:7:in `<top (required)>'
	13: from /var/lib/gems/2.5.0/gems/thor-0.20.3/lib/thor/base.rb:466:in `start'
	12: from /var/lib/gems/2.5.0/gems/thor-0.20.3/lib/thor.rb:387:in `dispatch'
	11: from /var/lib/gems/2.5.0/gems/thor-0.20.3/lib/thor/invocation.rb:126:in `invoke_command'
	10: from /var/lib/gems/2.5.0/gems/thor-0.20.3/lib/thor/command.rb:27:in `run'
	 9: from /var/lib/gems/2.5.0/gems/thor-0.20.3/lib/thor.rb:238:in `block in subcommand'
	 8: from /var/lib/gems/2.5.0/gems/thor-0.20.3/lib/thor/invocation.rb:115:in `invoke'
	 7: from /var/lib/gems/2.5.0/gems/thor-0.20.3/lib/thor.rb:387:in `dispatch'
	 6: from /var/lib/gems/2.5.0/gems/thor-0.20.3/lib/thor/invocation.rb:126:in `invoke_command'
	 5: from /var/lib/gems/2.5.0/gems/thor-0.20.3/lib/thor/command.rb:27:in `run'
	 4: from /var/lib/gems/2.5.0/gems/gl-0.3.1/lib/gl/cli/merge_requests.rb:11:in `list'
	 3: from /var/lib/gems/2.5.0/gems/tty-box-0.4.0/lib/tty/box.rb:100:in `frame'
	 2: from /var/lib/gems/2.5.0/gems/tty-box-0.4.0/lib/tty/box.rb:100:in `times'
	 1: from /var/lib/gems/2.5.0/gems/tty-box-0.4.0/lib/tty/box.rb:112:in `block in frame'
/var/lib/gems/2.5.0/gems/tty-box-0.4.0/lib/tty/box.rb:112:in `*': negative argument (ArgumentError)

Describe your environment

  • OS version: Ubuntu 18.04.3
  • Ruby version: 2.5.1p57
  • TTY::Box version: 0.4.0

TTY::Box.frame with a static width fails if the text joins multiple Pastel styled strings

Describe the problem

This sounds a lot like #5, but the error is different. When I frame a box with a static width and pass a string containing two pastel strings as the content, an IndexError is raised.

Steps to reproduce the problem

require "pastel"
require "tty-box"

pastel = Pastel.new
box = TTY::Box

print box.frame(
  "#{pastel.italic("TEXT TEXT TEXT TEXT")}; #{pastel.italic("TEXT TEXT TEXT TEXT")}",
  width: 40,
)

Actual behaviour

Error raised

/Users/adam/.gem/ruby/3.0.3/gems/strings-0.2.1/lib/strings/wrap.rb:158:in `insert': index 20 out of string (IndexError)
	from /Users/adam/.gem/ruby/3.0.3/gems/strings-0.2.1/lib/strings/wrap.rb:158:in `block in insert_ansi'
	from /Users/adam/.gem/ruby/3.0.3/gems/strings-0.2.1/lib/strings/wrap.rb:142:in `reverse_each'
	from /Users/adam/.gem/ruby/3.0.3/gems/strings-0.2.1/lib/strings/wrap.rb:142:in `insert_ansi'
	from /Users/adam/.gem/ruby/3.0.3/gems/strings-0.2.1/lib/strings/wrap.rb:111:in `format_line'
	from /Users/adam/.gem/ruby/3.0.3/gems/strings-0.2.1/lib/strings/wrap.rb:30:in `block in wrap'
	from /Users/adam/.gem/ruby/3.0.3/gems/strings-0.2.1/lib/strings/wrap.rb:29:in `map'
	from /Users/adam/.gem/ruby/3.0.3/gems/strings-0.2.1/lib/strings/wrap.rb:29:in `wrap'
	from /Users/adam/.gem/ruby/3.0.3/gems/tty-box-0.7.0/lib/tty/box.rb:337:in `block in format'
	from /Users/adam/.gem/ruby/3.0.3/gems/tty-box-0.7.0/lib/tty/box.rb:336:in `each'
	from /Users/adam/.gem/ruby/3.0.3/gems/tty-box-0.7.0/lib/tty/box.rb:336:in `each_with_object'
	from /Users/adam/.gem/ruby/3.0.3/gems/tty-box-0.7.0/lib/tty/box.rb:336:in `format'
	from /Users/adam/.gem/ruby/3.0.3/gems/tty-box-0.7.0/lib/tty/box.rb:219:in `frame'
	from test.rb:7:in `<main>'

Expected behaviour

The too-long string is wrapped inside the box, like an unstyled string

Describe your environment

  • OS version: macOS 13.3.1 (a)
  • Ruby version: 3.0.3p157
  • TTY::Box version: 0.7.0

TTY::Box.frame fails if the text includes Pastel styled strings

Describe the problem

When using TTY::Box with Pastel colored string an exception is raised. It seems like the frame algorithm is treating strings like \e[32mADS\e[0m improperly.

Steps to reproduce the problem

TTY::Box.frame { Pastel.new.green("ADS") }

Actual behaviour

ArgumentError: negative argument
from /Users/user/.rvm/gems/ruby-2.3.3/gems/tty-box-0.3.0/lib/tty/box.rb:112:in `*'

Expected behaviour

Writing a colored text in the box.

Describe your environment

  • OS version: Ubuntu 18.04.1
  • Ruby version: 2.5.1
  • TTY::Box version: 0.3.0

When specifying background, the box is not fully painted

Describe the problem

Given the following configuration for the TTY::Box.frame:

{
  align:        :left,
  border:       :round,
  enable_color: true,
  padding:      1,
  style:        {
    bg:     :yellow,
    fg:     :black,
    border: {
      fg: :red,
      bg: :yellow
    },
  },
  width:        114
}

The following is printed to my iTerm window:

image

Steps to reproduce the problem

style_args = {
  align:        :left,
  border:       :round,
  enable_color: true,
  padding:      1,
  style:        {
    bg:     :yellow,
    fg:     :black,
    border: {
      fg: :red,
      bg: :yellow
    },
  },
  width:        114
}

TTY::Box.frame(content, **style_args, &block)

Actual behavior

The window is printed, but the background does not fully pain the rectangle.

Expected behaviour

Full rectangle of the same background color.

Describe your environment

  • OS version:
  • Ruby version:
  • TTY::Box version:

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.