Module: Unmagic::Browser::Console::Terminal

Defined in:
lib/unmagic/browser/console/terminal.rb

Overview

What this terminal can do, and how to draw on it.

Constant Summary collapse

RESET =

Back to the terminal's own colours.

"\e[0m"
DIM =

For anything secondary — paths, sizes, counts.

"\e[2m"
BOLD =

For the one thing on the line that matters.

"\e[1m"
GREY =

For a session that's gone.

"\e[38;5;245m"
GREEN =

For a session that's live, and for headings.

"\e[38;5;114m"
BLUE =

For elements, keys, and the verbs in the banner.

"\e[38;5;75m"
YELLOW =

For what the terminal can't draw and had to write to a file instead.

"\e[38;5;179m"
WINSIZE_REQUEST =

TIOCGWINSZ is the only way to ask a terminal how big a character cell is in pixels, which is what an image has to be measured against. The request number is baked into each kernel's headers rather than standardised.

{
  /darwin|bsd/ => 0x40087468,
  /linux/ => 0x5413,
}.find { |pattern, _| pattern.match?(RUBY_PLATFORM) }&.last
FALLBACK_CELL =

A sane guess for a cell when the terminal won't say.

[8, 17].freeze

Class Method Summary collapse

Class Method Details

.cellArray(Integer, Integer)

Returns the width and height of one character cell, in pixels.

Returns:

  • (Array(Integer, Integer))

    the width and height of one character cell, in pixels



73
74
75
76
77
78
# File 'lib/unmagic/browser/console/terminal.rb', line 73

def cell
  rows, cols, width, height = window
  return FALLBACK_CELL if width.nil? || width.zero? || cols.zero? || rows.zero?

  [width / cols, height / rows]
end

.columnsInteger

Returns the terminal's width in characters.

Returns:

  • (Integer)

    the terminal's width in characters



66
67
68
69
70
# File 'lib/unmagic/browser/console/terminal.rb', line 66

def columns
  IO.console&.winsize&.last || 80
rescue StandardError
  80
end

.dim(text) ⇒ String

Returns the text, dimmed, if the terminal has colour.

Parameters:

  • text (String)

Returns:

  • (String)

    the text, dimmed, if the terminal has colour



82
83
84
# File 'lib/unmagic/browser/console/terminal.rb', line 82

def dim(text)
  paint(DIM, text)
end

.graphics?Boolean

Terminals that implement the kitty graphics protocol. Ghostty and WezTerm both do, and neither says "kitty" anywhere in $TERM.

Returns:

  • (Boolean)

    whether an image can be drawn inline



53
54
55
56
57
58
# File 'lib/unmagic/browser/console/terminal.rb', line 53

def graphics?
  return false unless tty?
  return true if ENV["KITTY_WINDOW_ID"] || ENV["WEZTERM_PANE"]

  "#{ENV["TERM"]} #{ENV["TERM_PROGRAM"]}".downcase.match?(/kitty|ghostty|wezterm/)
end

.paint(colour, text) ⇒ String

Returns the text in that colour, if the terminal has colour.

Parameters:

  • colour (String)

    one of the escape constants above

  • text (String)

Returns:

  • (String)

    the text in that colour, if the terminal has colour



89
90
91
# File 'lib/unmagic/browser/console/terminal.rb', line 89

def paint(colour, text)
  tty? ? "#{colour}#{text}#{RESET}" : text.to_s
end

.tty?Boolean

Returns whether output is going to a terminal at all.

Returns:

  • (Boolean)

    whether output is going to a terminal at all



61
62
63
# File 'lib/unmagic/browser/console/terminal.rb', line 61

def tty?
  $stdout.tty?
end

.windowArray(Integer, Integer, Integer, Integer)

Returns rows, columns, and the window's width and height in pixels; zeroes when the terminal won't say.

Returns:

  • (Array(Integer, Integer, Integer, Integer))

    rows, columns, and the window's width and height in pixels; zeroes when the terminal won't say



96
97
98
99
100
101
102
103
104
# File 'lib/unmagic/browser/console/terminal.rb', line 96

def window
  return [0, 0, 0, 0] unless WINSIZE_REQUEST && tty?

  buffer = [0, 0, 0, 0].pack("S_4")
  $stdout.ioctl(WINSIZE_REQUEST, buffer)
  buffer.unpack("S_4")
rescue StandardError
  [0, 0, 0, 0]
end