Welcome to The ANSI Hansi. This is your Hipster ANSI color library.
It allows you to produce colorized console output.
Many ANSI color libraries are already out there. However, this library tackles a few issues most of them have:
- It supports 8 color, 16 color, 88 color, 256 color and 24 bit (True Color) mode.
- It transparently converts colors between modes without you having to worry about this. It uses the YUV distance (rather than the RGB distance) to closer match how our brain perceives color similarity.
- It supports proper color nesting.
- It can automatically detect how many colors the current terminal supports.
- It does not enforce a DSL to be used (in fact, it does not include a DSL, but it makes it really easy to build your own DSL).
- Makes it easy to define string templates for highlighting, but doesn't force you to use them.
- It supports themes, but doesn't force you to use them. Themes make semantic coloring easier.
- It supports converting ANSI colors to CSS rules. It does not support parsing an ANSI colored string into HTML, this would be outside the scope of this library.
- It has zero dependencies and does not include a native extension.
- It does not monkey patch anything.
The main API entry points are Hansi.render(input)
, which will generate a colorized string, depending on the input, and Hansi[input]
, which will give you a color object. You can use Hansi for your purposes by just using one of the two, but they give you different levels of access.
The following code:
require 'hansi'
# simple usage
puts Hansi.render(:red, "WARNING!!")
puts Hansi.render("Hello *world*!", "*" => :bold)
# generate some colors
steps = (0..255).step(15)
render = -> options { print Hansi.render(Hansi[options], ':') }
steps.each do |red|
steps.each { |green| render[ red: red, green: green ]}
steps.each { |blue| render[ red: red, green: 255 - blue, blue: blue]}
steps.each { |blue| render[ red: red, blue: 255 - blue ]}
puts
end
Will result in output similar to this screenshot (if your terminal supports true color):
You can render a string in a given color:
puts Hansi.render(:red, "this is red")
red = Hansi["#f00"]
puts Hansi.render(red, "this is red")
You can render an s-expression style nested array (good for building your own DSL on top):
sexp = [:red, "Hello", [:yellow, ENV["USER"]], "- how are you?"]
puts Hansi.render(sexp, join: " ")
It is also possible to use template strings. These can use simple markup (anything enclosed between certain characters), or HTML style tags, or a combination of the two.
# a simple template
puts Hansi.render("foo *bar* _blah_", "*" => :red, "_" => :green)
# escaping a character
puts Hansi.render('foo *bar* _blah\_blah_', "*" => :red, "_" => :green)
# using tags, with interpolation
puts Hansi.render("<gold>Hello <underline>%s</underline>!</gold>", ENV['USER'], tags: true)
You can also use render
to turn a color object into its ANSI code.
The render
method takes a mode
option to enforce a color mode.
color = Hansi["#f80"]
Hansi.render(color, mode: 256) # => "\e[38;5;208m"
Hansi.render(color, mode: 16) # => "\e[33m"
The render method takes a theme
option. This option can have one of the following values:
- An instance of
Hansi::Theme
. - A symbol for a predefined theme (currently
:default
or:solarized
). - A hash mapping rules (symbols) to other rules or color values.
- An array of any of the above.
my_theme = Hansi::Theme.new(foo: :yellow)
puts Hansi.render("<foo>hi</foo>", tags: true, theme: my_theme) # bright yellow
puts Hansi.render("<foo>hi</foo>", tags: true, theme: { foo: :yellow }) # bright yellow
puts Hansi.render("<foo>hi</foo>", tags: true, theme: [:solarized, my_theme]) # solarized yellow
puts Hansi.render("<yellow>hi</yellow>", tags: true, theme: :solarized) # solarized yellow
When creating a theme, you can also pass in other themes to inherit rules from:
my_theme = Hansi::Theme.new(:solarized, em: :base0, b: :base1)
puts Hansi.render("This <em>is</em> <b>important</b>!", theme: my_theme)
It is also possible to register your theme globally:
Hansi::Theme[:my_theme] = Hansi::Theme.new(:solarized, em: :base0, b: :base1)
puts Hansi.render("This <em>is</em> <b>important</b>!", theme: :my_theme)
You can get access to a color object via Hansi[]
or Hansi::Theme#[]
.
Hansi[:yellow].red # => 255
Hansi::Theme[:solarized][:yellow].red # => 181
Hansi["#ff8300"].to_ansi(mode: 256) # => "\e[38;5;208m"
Hansi["#ff8300"].to_web_name # => :darkorange
Hansi["#ff8300"].to_web_name(exact: true) # => nil
You can also use a color object to find the closest color in a set of colors:
colors = [
Hansi[:red],
Hansi[:green],
Hansi[:blue],
Hansi[:orange]
]
Hansi[:yellow].closest(colors) # => Hansi[:orange]
You can override the color mode Hansi has detected for the current terminal:
puts "Detected colors: %d" % Hansi.mode
Hansi.mode = Hansi::TRUE_COLOR
Rather than defining a DSL, Hansi aims to be easily integrated with whatever tooling you use for building command line applications.
Combining for instance the s-expression style rendering with Hansi.color_names
makes creating a method name based DSL straightforward:
module ColorDSL
def color(*args)
Struct.new(:to_ary, :to_s).new(args, Hansi.render(args))
end
Hansi.color_names.each do |name|
define_method(name) { |*args| color(name, *args) }
end
end
extend ColorDSL
puts "Hello #{red("w", green("o", blue("r"), "l"), "d")}!"
Hansi does not turn ANSI escape codes into HTML for you, this would be outside of the scope of this project. However, depending on your use case, you might be able to generate semantic HTML yourself from whichever data structure you use.
In this case, Hansi can generate CSS rules for you.
Hansi[:red].to_css_rule # => "color: #ff0000;"
It can also generate a full stylesheet for a theme:
my_theme = Hansi::Theme.new(headline: :green, text: :springgreen, aside: :text, important: :bold)
puts my_theme.to_css
.headline {
color: #008000;
}
.text, .aside {
color: #00ff7f;
}
.important {
font-weight: bold;
}
You can pass a block for generating the CSS selector for a given rule name:
puts my_theme.to_css { |name| ".hansi .#{name}" }
.hansi .headline {
color: #008000;
}
.hansi .text, .hansi .aside {
color: #00ff7f;
}
.hansi .important {
font-weight: bold;
}