Project

tint_me

0.0
No release in over 3 years
A Ruby library for terminal text styling with ANSI colors and effects. Provides an elegant, functional API with immutable style objects that can be composed using the >> operator. Supports standard colors, hex values, and comprehensive text effects including bold, faint, italic, underline, overline, blink, inverse, and concealed text. Features type-safe argument validation using dry-schema and dry-types.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Runtime

~> 1.13
~> 1.7
~> 2.6
 Project Readme

💄 TIntMe! 💅

Gem Version CI License: MIT Gem Downloads Depfu

A Ruby library for terminal text styling with ANSI colors and effects. TIntMe! provides an elegant and composable API for applying colors, text decorations, and formatting to terminal output.

Features

  • Rich Color Support: Foreground and background colors with support for standard colors and hex values
  • Text Effects: Bold, faint, italic, underline (including double), overline, blink, inverse, and concealed text
  • Style Composition: Combine multiple styles using the >> operator for layered styling
  • Type Safety: Comprehensive argument validation using dry-schema and dry-types
  • Immutable Design: All style operations return new instances, making them safe for concurrent use
  • Zeitwerk Integration: Automatic loading with proper module organization
  • Positional Arguments: Concise syntax with TIntMe[:red, :bold] for streamlined styling

Installation

Add this line to your application's Gemfile:

gem "tint_me"

And then execute:

bundle install

Or install it yourself as:

gem install tint_me

Usage

Basic Styling

TIntMe supports both keyword arguments and positional arguments for creating styles. Here are examples using keyword arguments:

require 'tint_me'

# Create a style with keyword arguments
red_style = TIntMe[foreground: :red]
puts red_style.call("Hello World")

# Using the shortcut syntax
blue_style = TIntMe[foreground: :blue, bold: true]
puts blue_style["Hello World"]

Positional Arguments

TIntMe supports a concise positional argument syntax for common styling scenarios:

Supported Positional Arguments

Colors (applied as foreground):

  • Color symbols: :red, :green, :blue, :yellow, :magenta, :cyan, :white, :black, :gray
  • Bright colors: :bright_red, :bright_green, :bright_blue, etc.
  • Special values: :default, :reset
  • Hex strings: "#FF0000", "FF0000", "#F00", "F00"

Boolean flags (set to true):

  • :bold, :faint, :italic, :underline, :overline, :blink, :inverse, :conceal

Behavior Rules

# Two colors: first is foreground, second is background
TIntMe[:red, :yellow]                        # => foreground: :red, background: :yellow

# Too many colors: error
TIntMe[:red, :blue, :green]                  # => ArgumentError: Too many color arguments

# Duplicate flags: idempotent (no error)  
TIntMe[:bold, :italic, :bold]                # => bold: true, italic: true

# Keyword arguments override positional
TIntMe[:red, foreground: :blue]              # => foreground: :blue
TIntMe[:red, :yellow, background: :green]    # => foreground: :red, background: :green
TIntMe[:bold, bold: false]                   # => bold: false

# Mix freely for complex styling
TIntMe[:red, :yellow, :bold, underline: :double]  # => foreground: :red, background: :yellow, bold: true, underline: :double

Color Options

# Standard colors (keyword arguments)
TIntMe[foreground: :red]
TIntMe[background: :yellow]

# Standard colors (positional arguments)
TIntMe[:red]                          # Foreground color
TIntMe[:bright_blue]                  # Bright colors supported

# Hex colors (keyword arguments)
TIntMe[foreground: "#FF0000"]
TIntMe[background: "#00FF00"]
TIntMe[foreground: "FF0000"]

# Hex colors (positional arguments)
TIntMe["#FF0000"]                                    # 6-digit with hash
TIntMe["FF0000"]                                     # 6-digit without hash
TIntMe["#F00"]                                       # 3-digit with hash
TIntMe["F00"]                                        # 3-digit without hash

# Mix positional and keyword arguments
TIntMe[:red, background: :yellow]                    # Red foreground, yellow background
TIntMe["#00FF00", :bold, background: :black]         # Green foreground, bold, black background

Text Effects

# Individual effects (keyword arguments)
TIntMe[bold: true]
TIntMe[faint: true]                   # Faint/dim text
TIntMe[italic: true]
TIntMe[underline: true]
TIntMe[underline: :double]            # Double underline
TIntMe[overline: true]                # Overline decoration
TIntMe[blink: true]                   # Blinking text
TIntMe[inverse: true]                 # Reverse colors
TIntMe[conceal: true]                 # Hidden/concealed text

# Individual effects (positional arguments)
TIntMe[:bold]                         # Bold text
TIntMe[:italic]                       # Italic text
TIntMe[:underline]                    # Underlined text

# Multiple effects (keyword arguments)
TIntMe[foreground: :green, bold: true, underline: true]

# Multiple effects (positional arguments)
TIntMe[:bold, :italic, :underline]                   # Multiple boolean flags
TIntMe[:red, :bold, :italic]                         # Color + effects
TIntMe[:red, :yellow, :bold, :italic]                # Foreground + background + effects
TIntMe["#FF5733", :bold, :underline, :blink]         # Hex color + effects

# Mixed approaches
TIntMe[:bold, :italic, background: :yellow]          # Positional flags + keyword background
TIntMe[:red, :yellow, underline: :double]            # Positional colors + keyword underline
TIntMe[:red, :bold, background: :blue]               # Positional color/flag + keyword background

Style Composition

# Base styling
base = TIntMe[foreground: :blue]
emphasis = TIntMe[bold: true, underline: true]

# Combine styles (right-hand style takes precedence)
combined = base >> emphasis
puts combined.call("Styled text")

# Chain multiple compositions
final = base >> emphasis >> TIntMe[background: :white]

⚡ Performance Considerations

TIntMe is optimized for reusable styles through SGR sequence pre-computation. The composition operator (>>) should be used thoughtfully:

# ✅ RECOMMENDED: Pre-compose and reuse
ERROR_STYLE = TIntMe[foreground: :red] >> TIntMe[bold: true]
# OR using positional arguments (equivalent)
ERROR_STYLE = TIntMe[:red] >> TIntMe[:bold]
ERROR_STYLE.call("Error message")            # Fast: ~4.8M operations/sec

# ❌ AVOID: Runtime composition
(TIntMe[foreground: :red] >> TIntMe[bold: true]).call("Error")  # Slow: ~0.01M ops/sec
(TIntMe[:red] >> TIntMe[:bold]).call("Error")                   # Also slow: ~0.01M ops/sec

Key Guidelines:

  • Use >> for initialization: Create composed styles once and reuse them
  • Avoid runtime composition: Don't chain >> operators inside loops or frequently-called methods
  • For one-time styling: Consider alternatives like Paint gem for better dynamic performance
  • Each >> operation: Creates new Style instances and recalculates SGR sequences

Performance Comparison:

  • Pre-computed styles: ~4.8M operations/sec (fastest)
  • Runtime composition: ~0.01M operations/sec (246x slower)
  • Direct Style.new: ~0.03M operations/sec (71x slower)

🎯 When to Use TIntMe vs Alternatives

TIntMe excels at:

# Terminal UI frameworks with predefined styles
UI_STYLES = {
  error:   TIntMe[:red, :bold],                      # Concise positional syntax
  success: TIntMe[foreground: :green, bold: true],   # Or keyword arguments
  warning: TIntMe[:yellow] >> TIntMe[:bold],         # Or use composition (combining styles)
  info:    TIntMe[:blue, :italic]
}

def show_error(msg)
  puts UI_STYLES[:error].call(msg)          # Extremely fast: ~4.8M ops/sec
end

Consider alternatives for:

# Dynamic styling (use Paint gem instead)
texts.each { |text| Paint[text, :red, :bold] }          # ~2M ops/sec

# One-time styling with readable syntax (use Rainbow gem)
puts Rainbow("Success").green.bold                      # ~0.5M ops/sec

# Avoid with TIntMe - creates unnecessary overhead
texts.each { |text| (red >> bold).call(text) }          # Only ~0.01M ops/sec

Design Philosophy: TIntMe is intentionally optimized for the "define once, use many" pattern through SGR sequence pre-computation at initialization time. The performance characteristics guide you toward the most efficient usage patterns, where a small set of predefined styles serves many styling operations.

Method Aliases

style = TIntMe[foreground: :red, bold: true]

# All of these are equivalent
puts style.call("Hello")
puts style["Hello"]
puts style.("Hello")                  # Callable syntax

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 update the CHANGELOG.md, then create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org. However, there are GitHub Actions workflows set up to automate these processes, so please use those instead. See RELEASING.md for details.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/sakuro/tint_me.

License

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