Module: Terminal::Ansi
- Defined in:
- lib/terminal/ansi.rb,
lib/terminal/ansi/named_colors.rb,
lib/terminal/ansi/screen_viewer.rb
Overview
ANSI escape code generation and BBCode-to-ANSI conversion.
Provides methods for text decoration, color, cursor control, screen manipulation, hyperlinks, notifications, and progress indicators. All methods return ANSI escape sequence strings — they do not write to the terminal directly.
Colors can be specified as named symbols, 256-color indices, hex strings, or CSS/X11 color names.
Defined Under Namespace
Classes: ScreenViewer
Class Attribute Summary collapse
-
.attributes ⇒ Array<Symbol>
readonly
All known text attribute names (bold, italic, underline, etc.).
-
.colors ⇒ Array<Symbol>
readonly
All known basic color names.
-
.named_colors ⇒ Array<Symbol>
readonly
All CSS/X11 named colors.
Text Attribute Code Generation collapse
-
.[](*attributes) ⇒ String
Generate an ANSI escape sequence from attribute names or color indices.
-
.decorate(str, *attributes, reset: true) ⇒ String
Wrap a string with ANSI escape codes.
-
.rainbow(str, frequency: 0.3, spread: 0.8, seed: 1.1) ⇒ String
Generate rainbow-colored text using true color (24-bit) ANSI codes.
-
.try_convert(attributes, separator: ' ') ⇒ String?
Try to convert a space-separated attribute string to an ANSI escape sequence.
-
.undecorate(str) ⇒ String
Remove all ANSI escape codes from a string.
BBCode Generation collapse
-
.bbcode(str) ⇒ String
Convert BBCode markup to ANSI escape codes.
-
.escape_bbcode(str) ⇒ String
Escape BBCode tags so they render as literal text after Ansi.bbcode processing.
-
.unbbcode(str) ⇒ String
Remove BBCode tags from a string, keeping the enclosed text.
Tool Functions collapse
-
.ansi?(str) ⇒ true, false
Test whether a string contains ANSI escape codes.
-
.plain(str) ⇒ String
Remove both BBCode tags and ANSI escape codes, returning plain text.
-
.valid?(*attributes) ⇒ true, false
Check whether all given attributes are valid.
Cursor and Screen Control Codes collapse
-
.char_repeat(count = 1) ⇒ String
Repeat the last printed character.
-
.cursor_back(columns = 1) ⇒ String
Move cursor left.
-
.cursor_column(column = 1) ⇒ String
Move cursor to absolute column.
-
.cursor_column_rel(column = 1) ⇒ String
Move cursor right by relative columns.
-
.cursor_down(lines = 1) ⇒ String
Move cursor down.
-
.cursor_forward(columns = 1) ⇒ String
Move cursor right.
-
.cursor_hide ⇒ String
Hide cursor.
-
.cursor_next_line(lines = 1) ⇒ String
Move cursor to beginning of line, N lines down.
-
.cursor_pos(row, column = nil) ⇒ String
Move cursor to absolute position.
-
.cursor_prev_line(lines = 1) ⇒ String
Move cursor to beginning of line, N lines up.
-
.cursor_restore_pos ⇒ String
Restore previously saved cursor position.
-
.cursor_row_rel(row = 1) ⇒ String
Move cursor down by relative rows.
-
.cursor_save_pos ⇒ String
Save current cursor position.
-
.cursor_show ⇒ String
Show cursor.
-
.cursor_up(lines = 1) ⇒ String
Move cursor up.
-
.line_erase(part = :all) ⇒ String
Erase part of the current line.
-
.screen_alternate ⇒ String
Switch to the alternate screen buffer.
-
.screen_alternate_off ⇒ String
Switch back from the alternate screen buffer.
-
.screen_erase(part = :all) ⇒ String
Erase part of the screen.
-
.screen_restore ⇒ String
Restore previously saved screen state.
-
.screen_save ⇒ String
Save screen state.
-
.screen_scroll_down(lines = 1) ⇒ String
Scroll the screen down.
-
.screen_scroll_up(lines = 1) ⇒ String
Scroll the screen up.
Terminal Integration Codes collapse
-
.clipboard(text) ⇒ String
Copy text to clipboard.
-
.link(url, text = nil, **params) ⇒ String
Create or start a hyperlink.
-
.link_end ⇒ String
End a hyperlink.
-
.notify(text) ⇒ String
Send a desktop notification via the terminal.
-
.progress(state, percent = 0) ⇒ String
Show or update a progress indicator in the terminal tab/title bar.
-
.scale(text, scale: nil, width: nil, fracn: nil, fracd: nil, vertical: nil, horizontal: nil) ⇒ String
Scale text using the text sizing protocol.
-
.status(text: nil, detail: nil, color: nil, indicator: nil) ⇒ String
Show or update the session status of the terminal.
-
.status_reset ⇒ String
Reset session status of the terminal.
-
.title(title) ⇒ String
Set the terminal window title.
Class Attribute Details
.attributes ⇒ Array<Symbol> (readonly)
All known text attribute names (bold, italic, underline, etc.).
27 |
# File 'lib/terminal/ansi.rb', line 27 def attributes = @attributes.dup |
.colors ⇒ Array<Symbol> (readonly)
All known basic color names.
33 |
# File 'lib/terminal/ansi.rb', line 33 def colors = @colors.dup |
.named_colors ⇒ Array<Symbol> (readonly)
All CSS/X11 named colors.
39 |
# File 'lib/terminal/ansi.rb', line 39 def named_colors = NAMED_COLORS.keys.map!(&:to_sym) |
Class Method Details
.[](*attributes) ⇒ String
Generate an ANSI escape sequence from attribute names or color indices.
Accepts symbols (:bold, :red), strings ("bold", "#ff0000"),
or integers for 256-color palette (0-255 foreground, 256-511
background, 512-767 underline color).
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/terminal/ansi.rb', line 65 def [](*attributes) return +'' if attributes.empty? "\e[#{ attributes .map! do |arg| next @attr_str_map[arg] || _invalid(arg) if arg.is_a?(String) next @attr_sym_map[arg] || _invalid(arg) if arg.is_a?(Symbol) _invalid(arg) unless arg.is_a?(Integer) next "38;5;#{arg}" if arg >= 0 && arg <= 255 next "48;5;#{arg - 256}" if arg >= 256 && arg <= 511 arg >= 512 && arg <= 767 ? "58;5;#{arg - 512}" : _invalid(arg) end .join(';') }m" end |
.ansi?(str) ⇒ true, false
Test whether a string contains ANSI escape codes.
253 |
# File 'lib/terminal/ansi.rb', line 253 def ansi?(str) = @re_test.match?(str.to_s) |
.bbcode(str) ⇒ String
Convert BBCode markup to ANSI escape codes.
Tags like [bold] are converted to their ANSI equivalents;
[/bold] or [/] resets. Unknown tags are left unchanged.
Escape a tag with a backslash: [\\bold] renders as [bold].
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/terminal/ansi.rb', line 180 def bbcode(str) str = str.to_s return str.dup unless str.index('[') str.gsub(@re_bbcode) do |match| hit = @bbcc[match] and next hit next "[#{match[2..]}" if (fc = match[1]) == '\\' @bbcc.clear if @bbcc.size == 1024 @bbcc[match] = if fc == ']' || (att = match[1..-2].split).empty? match else att = att.map { @attr_str_map[it] || break } att ? "\e[#{att.join(';')}m" : match end end end |
.char_repeat(count = 1) ⇒ String
Repeat the last printed character.
436 |
# File 'lib/terminal/ansi.rb', line 436 def char_repeat(count = 1) = "\e[#{count}b" |
.clipboard(text) ⇒ String
Supported by Ghostty, iTerm2, Kitty, Rio, Tabby, WezTerm.
Copy text to clipboard.
567 |
# File 'lib/terminal/ansi.rb', line 567 def clipboard(text) = "\e]52;c;#{[text.to_s].pack('m0')}\a" |
.cursor_back(columns = 1) ⇒ String
Move cursor left.
315 |
# File 'lib/terminal/ansi.rb', line 315 def cursor_back(columns = 1) = "\e[#{columns}D" |
.cursor_column(column = 1) ⇒ String
Move cursor to absolute column.
333 |
# File 'lib/terminal/ansi.rb', line 333 def cursor_column(column = 1) = "\e[#{column}G" |
.cursor_column_rel(column = 1) ⇒ String
Move cursor right by relative columns.
339 |
# File 'lib/terminal/ansi.rb', line 339 def cursor_column_rel(column = 1) = "\e[#{column}a" |
.cursor_down(lines = 1) ⇒ String
Move cursor down.
303 |
# File 'lib/terminal/ansi.rb', line 303 def cursor_down(lines = 1) = "\e[#{lines}B" |
.cursor_forward(columns = 1) ⇒ String
Move cursor right.
309 |
# File 'lib/terminal/ansi.rb', line 309 def cursor_forward(columns = 1) = "\e[#{columns}C" |
.cursor_hide ⇒ String
Hide cursor.
369 |
# File 'lib/terminal/ansi.rb', line 369 def cursor_hide = +CURSOR_HIDE |
.cursor_next_line(lines = 1) ⇒ String
Move cursor to beginning of line, N lines down.
321 |
# File 'lib/terminal/ansi.rb', line 321 def cursor_next_line(lines = 1) = "\e[#{lines}E" |
.cursor_pos(row, column = nil) ⇒ String
Move cursor to absolute position.
356 357 358 359 |
# File 'lib/terminal/ansi.rb', line 356 def cursor_pos(row, column = nil) return column ? "\e[;#{column}H" : "\e[H" unless row column ? "\e[#{row};#{column}H" : "\e[#{row}H" end |
.cursor_prev_line(lines = 1) ⇒ String
Move cursor to beginning of line, N lines up.
327 |
# File 'lib/terminal/ansi.rb', line 327 def cursor_prev_line(lines = 1) = "\e[#{lines}F" |
.cursor_restore_pos ⇒ String
Restore previously saved cursor position.
383 |
# File 'lib/terminal/ansi.rb', line 383 def cursor_restore_pos = +CURSOR_POS_RESTORE |
.cursor_row_rel(row = 1) ⇒ String
Move cursor down by relative rows.
345 |
# File 'lib/terminal/ansi.rb', line 345 def cursor_row_rel(row = 1) = "\e[#{row}e" |
.cursor_save_pos ⇒ String
Save current cursor position.
376 |
# File 'lib/terminal/ansi.rb', line 376 def cursor_save_pos = +CURSOR_POS_SAVE |
.cursor_show ⇒ String
Show cursor.
364 |
# File 'lib/terminal/ansi.rb', line 364 def cursor_show = +CURSOR_SHOW |
.cursor_up(lines = 1) ⇒ String
Move cursor up.
297 |
# File 'lib/terminal/ansi.rb', line 297 def cursor_up(lines = 1) = "\e[#{lines}A" |
.decorate(str, *attributes, reset: true) ⇒ String
Wrap a string with ANSI escape codes.
95 96 97 98 |
# File 'lib/terminal/ansi.rb', line 95 def decorate(str, *attributes, reset: true) attributes = self[*attributes] attributes.empty? ? "#{str}" : "#{attributes}#{str}#{"\e[m" if reset}" end |
.escape_bbcode(str) ⇒ String
Escape BBCode tags so they render as literal text after bbcode processing.
228 229 230 231 232 233 234 235 236 237 |
# File 'lib/terminal/ansi.rb', line 228 def escape_bbcode(str) str = str.to_s return str.dup unless str.index('[') str.gsub(@re_bbcode) do |match| hit = @bbcc[match] and hit[1] == '\\' ? hit : "[\\#{match[1..]}" next match if (fc = match[1]) == '\\' || fc == ']' next match if (att = match[1..-2].split).empty? att.map { @attr_str_map[it] || break } ? "[\\#{match[1..]}" : match end end |
.line_erase(part = :all) ⇒ String
Erase part of the current line.
443 |
# File 'lib/terminal/ansi.rb', line 443 def line_erase(part = :all) = "\e[#{@line_erase[part]}K" |
.link(url, text = nil, **params) ⇒ String
Supported by Ghostty, iTerm2, Kitty, Rio, Tabby, WezTerm.
Create or start a hyperlink.
546 547 548 549 550 551 552 |
# File 'lib/terminal/ansi.rb', line 546 def link(url, text = nil, **params) start = "\e]8;#{ _no_bell(params.map { it.join('=') }.join(':')) };#{_no_bell(url)}\a" text ? "#{start}#{_no_bell(text)}#{LINK_END}" : start end |
.link_end ⇒ String
End a hyperlink.
559 |
# File 'lib/terminal/ansi.rb', line 559 def link_end = +LINK_END |
.notify(text) ⇒ String
Supported by Ghostty, iTerm2, Kitty, WezTerm.
Send a desktop notification via the terminal.
472 |
# File 'lib/terminal/ansi.rb', line 472 def notify(text) = "\e]9;#{_no_bell(text)}\a" |
.plain(str) ⇒ String
Remove both BBCode tags and ANSI escape codes, returning plain text.
281 282 283 284 285 |
# File 'lib/terminal/ansi.rb', line 281 def plain(str) str = unbbcode(str) str.gsub!(@re_test, '') if str.index("\e") str end |
.progress(state, percent = 0) ⇒ String
Supported by Ghostty, iTerm2, Kitty.
Show or update a progress indicator in the terminal tab/title bar.
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 |
# File 'lib/terminal/ansi.rb', line 491 def progress(state, percent = 0) case state when :show, true state = 1 when :err, :error state = 2 when :warn, :warning state = 4 when Numeric percent, state = state, 1 when :indeterminate return +PROGRESS_SHOW_INDETERMINATE else return +PROGRESS_HIDE end "\e]9;4;#{state};#{percent.to_i.clamp(0, 100)}\a" end |
.rainbow(str, frequency: 0.3, spread: 0.8, seed: 1.1) ⇒ String
Generate rainbow-colored text using true color (24-bit) ANSI codes.
143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/terminal/ansi.rb', line 143 def rainbow(str, frequency: 0.3, spread: 0.8, seed: 1.1) pos = -1 @pi2_third ||= 2.0 * Math::PI / 3.0 @pi4_third ||= 4.0 * Math::PI / 3.0 ( str.to_s.chars.map! do |char| i = (seed + ((pos += 1) / spread)) * frequency "\e[38;2;#{(Math.sin(i) * 255).round.abs};#{ (Math.sin(i + @pi2_third) * 255).round.abs };#{(Math.sin(i + @pi4_third) * 255).round.abs}m#{char}" end << RESET_FG ).join end |
.scale(text, scale: nil, width: nil, fracn: nil, fracd: nil, vertical: nil, horizontal: nil) ⇒ String
Supported by Kitty.
Scale text using the text sizing protocol.
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 |
# File 'lib/terminal/ansi.rb', line 621 def scale( text, scale: nil, width: nil, fracn: nil, fracd: nil, vertical: nil, horizontal: nil ) opts = scale ? ["s=#{scale.clamp(1, 7)}"] : [] opts << "w=#{width.clamp(0, 7)}" if width if fracn opts << "n=#{fracn = fracn.clamp(0, 15)}" opts << "d=#{fracd.clamp(fracn + 1, 15)}" if fracd case vertical when 0, :top opts << 'v=0' when 1, :bottom opts << 'v=1' when 2, :middle opts << 'v=2' end case horizontal when 0, :left opts << 'h=0' when 1, :right opts << 'h=1' when 2, :center opts << 'h=2' end end "\e]66;#{opts.join(':')};#{text}\a" end |
.screen_alternate ⇒ String
Switch to the alternate screen buffer.
411 |
# File 'lib/terminal/ansi.rb', line 411 def screen_alternate = +SCREEN_ALTERNATE |
.screen_alternate_off ⇒ String
Switch back from the alternate screen buffer.
418 |
# File 'lib/terminal/ansi.rb', line 418 def screen_alternate_off = +SCREEN_ALTERNATE_OFF |
.screen_erase(part = :all) ⇒ String
Erase part of the screen.
390 |
# File 'lib/terminal/ansi.rb', line 390 def screen_erase(part = :all) = "\e[#{@screen_erase[part]}J" |
.screen_restore ⇒ String
Restore previously saved screen state.
404 |
# File 'lib/terminal/ansi.rb', line 404 def screen_restore = +SCREEN_RESTORE |
.screen_save ⇒ String
Save screen state.
397 |
# File 'lib/terminal/ansi.rb', line 397 def screen_save = +SCREEN_SAVE |
.screen_scroll_down(lines = 1) ⇒ String
Scroll the screen down.
430 |
# File 'lib/terminal/ansi.rb', line 430 def screen_scroll_down(lines = 1) = "\e[#{lines}T" |
.screen_scroll_up(lines = 1) ⇒ String
Scroll the screen up.
424 |
# File 'lib/terminal/ansi.rb', line 424 def screen_scroll_up(lines = 1) = "\e[#{lines}S" |
.status(text: nil, detail: nil, color: nil, indicator: nil) ⇒ String
Supported by iTerm2.
Show or update the session status of the terminal.
584 585 586 587 588 589 590 |
# File 'lib/terminal/ansi.rb', line 584 def status(text: nil, detail: nil, color: nil, indicator: nil) str = text ? ";status=#{_no_bell(text)}" : '' indicator = _try_rgb(indicator) and str << ";indicator=#{indicator}" color = _try_rgb(color) and str << ";color=#{color}" str << "detail=#{_no_bell(detail)}" if detail str.empty? ? +STATUS_RESET : "\e]21337#{str}\a" end |
.status_reset ⇒ String
Reset session status of the terminal.
597 |
# File 'lib/terminal/ansi.rb', line 597 def status_reset = +STATUS_RESET |
.title(title) ⇒ String
Supported by iTerm2, Kitty, macOS Terminal, Tabby, WezTerm.
Set the terminal window title.
461 |
# File 'lib/terminal/ansi.rb', line 461 def title(title) = "\e]0;#{_no_bell(title)}\a" |
.try_convert(attributes, separator: ' ') ⇒ String?
Try to convert a space-separated attribute string to an ANSI escape sequence.
123 124 125 126 127 128 129 130 |
# File 'lib/terminal/ansi.rb', line 123 def try_convert(attributes, separator: ' ') return unless attributes unless (attributes = attributes.to_s.split(separator)).empty? # attributes.uniq! codes = attributes.map { @attr_str_map[it] || break } "\e[#{codes.join(';')}m" if codes end end |
.unbbcode(str) ⇒ String
Remove BBCode tags from a string, keeping the enclosed text.
205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/terminal/ansi.rb', line 205 def unbbcode(str) str = str.to_s return str.dup unless str.index('[') str.gsub(@re_bbcode) do |match| hit = @bbcc[match] and next hit[0] == '[' ? hit : nil next match if (fc = match[1]) == ']' next "[#{match[2..]}" if fc == '\\' next match if (att = match[1..-2].split).empty? match unless att.map { @attr_str_map[it] || break } end end |
.undecorate(str) ⇒ String
Remove all ANSI escape codes from a string.
108 109 110 |
# File 'lib/terminal/ansi.rb', line 108 def undecorate(str) (str = str.to_s).index("\e") ? str.gsub(@re_test, '') : str.dup end |
.valid?(*attributes) ⇒ true, false
Check whether all given attributes are valid.
264 265 266 267 268 269 270 |
# File 'lib/terminal/ansi.rb', line 264 def valid?(*attributes) attributes.all? do |arg| next @attr_str_map[arg] if arg.is_a?(String) next @attr_sym_map[arg] if arg.is_a?(Symbol) arg.is_a?(Integer) && arg >= 0 && arg <= 767 end end |