natty-ui 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'io/console'
4
- require_relative 'wrapper'
5
3
  require_relative 'ansi'
4
+ require_relative 'wrapper'
6
5
 
7
6
  module NattyUI
8
7
  class AnsiWrapper < Wrapper
@@ -110,18 +109,48 @@ module NattyUI
110
109
  MSG = Ansi[:bold, 231].freeze
111
110
  end
112
111
 
113
- class Framed < Framed
112
+ class Framed < Section
114
113
  protected
115
114
 
115
+ def initialize(parent, title:, type:, **opts)
116
+ @parent = parent
117
+ title = title.to_s.tr("\r\n", '')
118
+ topl, topr, botl, botr, hor, vert = *components(type)
119
+ width = available_width
120
+ rcount = [width - _plain_width(title) - 6, 0].max
121
+ parent.puts(
122
+ "#{COLOR}#{topl}#{hor}#{hor}#{Ansi.reset} " \
123
+ "#{TITLE_ATTR}#{title}#{Ansi.reset} " \
124
+ "#{COLOR}#{hor * rcount}#{topr}#{Ansi.reset}"
125
+ )
126
+ @bottom = "#{COLOR}#{botl}#{hor * (width - 2)}#{botr}#{Ansi.reset}"
127
+ vert = "#{COLOR}#{vert}#{Ansi.reset}"
128
+ super(
129
+ parent,
130
+ prefix: "#{vert} ",
131
+ suffix:
132
+ "#{Ansi.cursor_right_aligned}" \
133
+ "#{Ansi.cursor_left(suffix_width)}#{vert}",
134
+ **opts
135
+ )
136
+ end
137
+
138
+ def suffix = "#{super} "
139
+ def finish = parent.puts(@bottom)
140
+
116
141
  def components(type)
117
- top_start, top_suffix, left, bottom = super
118
- [
119
- "#{Ansi[39]}#{top_start}#{Ansi[:bold, 231]}",
120
- "#{Ansi[:reset, 39]}#{top_suffix}#{Ansi.reset}",
121
- Ansi.embellish(left, 39),
122
- Ansi.embellish(bottom, 39)
123
- ]
142
+ COMPONENTS[type] || raise(ArgumentError, "invalid frame type - #{type}")
124
143
  end
144
+
145
+ COLOR = Ansi[39].freeze
146
+ TITLE_ATTR = Ansi[:bold, 231].freeze
147
+ COMPONENTS = {
148
+ rounded: %w[╭ ╮ ╰ ╯ ─ │],
149
+ simple: %w[┌ ┐ └ ┘ ─ │],
150
+ heavy: %w[┏ ┓ ┗ ┛ ━ ┃],
151
+ semi: %w[┍ ┑ ┕ ┙ ━ │],
152
+ double: %w[╔ ╗ ╚ ╝ ═ ║]
153
+ }.compare_by_identity.freeze
125
154
  end
126
155
 
127
156
  class Ask < Ask
@@ -136,6 +165,14 @@ module NattyUI
136
165
  PREFIX = "#{Ansi[:bold, :italic, 220]}▶︎#{Ansi[:reset, 220]}".freeze
137
166
  end
138
167
 
168
+ class Request < Request
169
+ def prompt(question) = "#{prefix}#{PREFIX} #{question}#{Ansi.reset} "
170
+ def finish = (wrapper.stream << FINISH).flush
171
+
172
+ PREFIX = "#{Ansi[:bold, :italic, 220]}▶︎#{Ansi[:reset, 220]}".freeze
173
+ FINISH = (Ansi.cursor_line_up + Ansi.line_erase_to_end).freeze
174
+ end
175
+
139
176
  class Query < Query
140
177
  protected
141
178
 
@@ -155,9 +192,9 @@ module NattyUI
155
192
  class Progress < Progress
156
193
  protected
157
194
 
158
- def draw_title(title)
195
+ def draw(title)
159
196
  @prefix = "#{prefix}#{TITLE_PREFIX}#{title}#{Ansi.reset} "
160
- (wrapper.stream << @prefix).flush
197
+ (wrapper.stream << @prefix << Ansi.cursor_hide).flush
161
198
  @prefix = "#{Ansi.line_clear}#{@prefix}"
162
199
  if @max_value
163
200
  @prefix << BAR_COLOR
@@ -167,26 +204,21 @@ module NattyUI
167
204
  end
168
205
  end
169
206
 
170
- def draw_final = (wrapper.stream << Ansi.line_clear).flush
171
-
172
207
  def redraw
173
208
  (wrapper.stream << @prefix << (@max_value ? fullbar : indicator)).flush
174
209
  end
175
210
 
211
+ def end_draw = (wrapper.stream << ERASE).flush
176
212
  def indicator = '─╲│╱'[(@indicator += 1) % 4]
213
+ # def indicator = '⣷⣯⣟⡿⢿⣻⣽⣾'[(@indicator += 1) % 8]
177
214
 
178
215
  def fullbar
179
216
  percent = @value / @max_value
180
217
  count = (30 * percent).to_i
218
+ mv = max_value.to_i.to_s
181
219
  "#{'█' * count}#{BAR_BACK}#{'▁' * (30 - count)}" \
182
- "#{BAR_INK} #{
183
- format(
184
- '%<value>.0f/%<max_value>.0f (%<percent>.2f%%)',
185
- value: @value,
186
- max_value: @max_value,
187
- percent: percent * 100
188
- )
189
- }"
220
+ "#{BAR_INK} #{value.to_i.to_s.rjust(mv.size)}/#{mv} " \
221
+ "(#{(percent * 100).round(2).to_s.rjust(6)})"
190
222
  end
191
223
 
192
224
  TITLE_PREFIX = "#{Ansi[:bold, :italic, 117]}➔#{Ansi[:reset, 117]} ".freeze
@@ -194,6 +226,7 @@ module NattyUI
194
226
  BAR_COLOR = Ansi[39, 295].freeze
195
227
  BAR_BACK = Ansi[236, 492].freeze
196
228
  BAR_INK = Ansi[:bold, 255, :on_default].freeze
229
+ ERASE = (Ansi.line_clear + Ansi.cursor_show).freeze
197
230
  end
198
231
 
199
232
  PAGE_BEGIN =
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NattyUI
4
+ #
5
+ # Features of {NattyUI} - methods to display natty elements.
6
+ #
7
+ module Features
8
+ # Print a horizontal rule
9
+ #
10
+ # @param [#to_s] symbol string to build the horizontal rule
11
+ # @return [Wrapper, Wrapper::Element] itself
12
+ def hr(symbol = '═')
13
+ symbol = symbol.to_s
14
+ size = _plain_width(symbol)
15
+ return self if size.zero?
16
+ msg = symbol * ((available_width - 1) / size)
17
+ return puts(msg, prefix: Ansi[39], suffix: Ansi.reset) if wrapper.ansi?
18
+ puts(msg)
19
+ end
20
+
21
+ protected
22
+
23
+ def _plain_width(str) = NattyUI.display_width(NattyUI.plain(str))
24
+ def _blemish_width(str) = NattyUI.display_width(Ansi.blemish(str))
25
+
26
+ def _element(type, *args)
27
+ wrapper.class.const_get(type).__send__(:new, self).__send__(:_call, *args)
28
+ end
29
+
30
+ def _section(owner, type, args, **opts, &block)
31
+ sec = wrapper.class.const_get(type).__send__(:new, owner, **opts)
32
+ sec.puts(*args) if args && !args.empty?
33
+ block ? sec.__send__(:_call, &block) : sec
34
+ end
35
+ end
36
+ end
@@ -12,7 +12,7 @@ module NattyUI
12
12
  #
13
13
  module ProgressAttributes
14
14
  # @attribute [r] completed?
15
- # @return [Boolean] whether the task completed sucessfully
15
+ # @return [Boolean] whether the task completed successfully
16
16
  def completed? = (@status == :completed)
17
17
 
18
18
  # @attribute [r] failed?
@@ -2,5 +2,5 @@
2
2
 
3
3
  module NattyUI
4
4
  # @return [String] the version number of the gem
5
- VERSION = '0.5.2'
5
+ VERSION = '0.6.0'
6
6
  end
@@ -17,11 +17,13 @@ module NattyUI
17
17
  # when true
18
18
  # sec.info('Yeah!!')
19
19
  # when false
20
- # sec.write("That's pitty!")
20
+ # sec.write("That's pity!")
21
21
  # else
22
22
  # sec.failed('You should have an opinion!')
23
23
  # end
24
24
  #
25
+ # @see NattyUI.in_stream
26
+ #
25
27
  # @param question [#to_s] Question to display
26
28
  # @param yes [#to_s] chars which will be used to answer 'Yes'
27
29
  # @param no [#to_s] chars which will be used to answer 'No'
@@ -65,9 +67,9 @@ module NattyUI
65
67
 
66
68
  def grab(yes, no)
67
69
  yes = yes.to_s.chars.uniq
70
+ raise(ArgumentError, ':yes can not be empty') if yes.empty?
68
71
  no = no.to_s.chars.uniq
69
- raise(ArgumentError, ':yes can not be emoty') if yes.empty?
70
- raise(ArgumentError, ':no can not be emoty') if no.empty?
72
+ raise(ArgumentError, ':no can not be empty') if no.empty?
71
73
  return yes, no if (yes & no).empty?
72
74
  raise(ArgumentError, 'chars in :yes and :no can not be intersect')
73
75
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'features'
3
+ require_relative '../features'
4
4
 
5
5
  module NattyUI
6
6
  class Wrapper
@@ -24,8 +24,7 @@ module NattyUI
24
24
 
25
25
  # Close the element.
26
26
  #
27
- # @return [Element] itself when used without a code block
28
- # @return [nil] when used with a code block
27
+ # @return [Element] itself
29
28
  def close = _close(:closed)
30
29
 
31
30
  alias _to_s to_s
@@ -37,7 +36,10 @@ module NattyUI
37
36
  protected
38
37
 
39
38
  def prefix = "#{@parent.__send__(:prefix)}#{@prefix}"
40
- def suffix = "#{@parent.__send__(:suffix)}#{@suffix}"
39
+ def suffix = "#{@suffix}#{@parent.__send__(:suffix)}"
40
+ def prefix_width = _blemish_width(prefix)
41
+ def suffix_width = _blemish_width(suffix)
42
+ def available_width = wrapper.screen_columns - prefix_width - suffix_width
41
43
  def finish = nil
42
44
 
43
45
  def wrapper
@@ -14,7 +14,7 @@ module NattyUI
14
14
  # @param [Array<#to_s>] args more objects to print
15
15
  # @param [Symbol] type frame type;
16
16
  # valid types are `:rounded`, `:simple`, `:heavy`, `:semi`, `:double`
17
- # @yieldparam [Wrapper::Framed] section the created section
17
+ # @yieldparam [Wrapper::Framed] framed the created section
18
18
  # @return [Object] the result of the code block
19
19
  # @return [Wrapper::Framed] itself, when no code block is given
20
20
  def framed(title, *args, type: :rounded, &block)
@@ -7,7 +7,7 @@ module NattyUI
7
7
  # Creates section with a H1 title.
8
8
  #
9
9
  # @param (see #information)
10
- # @yieldparam [Wrapper::Heading] section the created section
10
+ # @yieldparam [Wrapper::Heading] heading the created section
11
11
  # @return [Object] the result of the code block
12
12
  # @return [Wrapper::Heading] itself, when no code block is given
13
13
  def h1(title, *args, &block)
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'element'
4
+
5
+ module NattyUI
6
+ module Features
7
+ #
8
+ # Print items of a given list as columns.
9
+ # In the default compact format columns may have diffrent widths and the
10
+ # list items are ordered column-wise.
11
+ # The non-compact format prints all columns in same width and order the list
12
+ # items row-wise.
13
+ #
14
+ # @param [Array<#to_s>] args items to print
15
+ # @param [Boolean] compact whether to use compact format
16
+ # @return [Wrapper, Wrapper::Element] itself
17
+ def ls(*args, compact: true) = _element(:ListInColumns, args, compact)
18
+ end
19
+
20
+ class Wrapper
21
+ #
22
+ # An {Element} to print items of a given list as columns.
23
+ #
24
+ # @see Features#ls
25
+ class ListInColumns < Element
26
+ protected
27
+
28
+ def _call(list, compact)
29
+ list.flatten!
30
+ return parent if list.empty?
31
+ list.map! { |item| Item.new(item = item.to_s, _plain_width(item)) }
32
+ if compact
33
+ each_compacted(list, available_width) { |line| parent.puts(line) }
34
+ else
35
+ each(list, available_width) { |line| parent.puts(line) }
36
+ end
37
+ parent
38
+ end
39
+
40
+ def each(list, max_width)
41
+ width = list.max_by(&:width).width + 3
42
+ list.each_slice(max_width / width) do |slice|
43
+ yield(slice.map { |item| item.to_s(width) }.join)
44
+ end
45
+ end
46
+
47
+ def each_compacted(list, max_width)
48
+ found, widths = find_columns(list, max_width)
49
+ fill(found[-1], found[0].size)
50
+ found.transpose.each do |row|
51
+ row = row.each_with_index.map { |item, col| item&.to_s(widths[col]) }
52
+ yield(row.join)
53
+ end
54
+ end
55
+
56
+ def find_columns(list, max_width)
57
+ found = [list]
58
+ widths = [list.max_by(&:width).width]
59
+ 1.upto(list.size - 1) do |slice_size|
60
+ candidate = list.each_slice(list.size / slice_size).to_a
61
+ cwidths = candidate.map { |ary| ary.max_by(&:width).width + 3 }
62
+ cwidths[-1] -= 3
63
+ break if cwidths.sum > max_width
64
+ found = candidate
65
+ widths = cwidths
66
+ end
67
+ [found, widths]
68
+ end
69
+
70
+ def fill(ary, size)
71
+ diff = size - ary.size
72
+ ary.fill(nil, ary.size, diff) if diff.positive?
73
+ end
74
+
75
+ Item =
76
+ Data.define(:str, :width) do
77
+ def to_s(in_width) = "#{str}#{' ' * (in_width - width)}"
78
+ end
79
+ end
80
+ end
81
+ end
@@ -10,7 +10,7 @@ module NattyUI
10
10
  # @param [#to_s] title object to print as section title
11
11
  # @param [Array<#to_s>] args more objects to print
12
12
  # @param [#to_s] symbol symbol/prefix used for the title
13
- # @yieldparam [Wrapper::Message] section the created section
13
+ # @yieldparam [Wrapper::Message] message the created section
14
14
  # @return [Object] the result of the code block
15
15
  # @return [Wrapper::Message] itself, when no code block is given
16
16
  def message(title, *args, symbol: :default, &block)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'element'
4
- require_relative 'mixins'
4
+ require_relative '../mixins'
5
5
 
6
6
  module NattyUI
7
7
  module Features
@@ -35,11 +35,11 @@ module NattyUI
35
35
  @max_value = [0, max_value.to_f].max if max_value
36
36
  @value = 0
37
37
  @progress = 0
38
- draw_title(title)
38
+ draw(title)
39
39
  end
40
40
 
41
- def draw_title(title) = (wrapper.stream << prefix << "➔ #{title} ").flush
42
- def draw_final = (wrapper.stream << "\n")
41
+ def draw(title) = (wrapper.stream << prefix << "➔ #{title} ").flush
42
+ def end_draw = (wrapper.stream << "\n")
43
43
 
44
44
  def redraw
45
45
  return (wrapper.stream << '.').flush unless @max_value
@@ -50,10 +50,15 @@ module NattyUI
50
50
  end
51
51
 
52
52
  def finish
53
- draw_final
53
+ end_draw
54
54
  return @parent.failed(*@final_text) if failed?
55
- @status = :ok if @status == :closed
56
- @parent.completed(*@final_text)
55
+ _section(
56
+ @parent,
57
+ :Message,
58
+ @final_text,
59
+ title: @final_text.shift,
60
+ symbol: @status = :completed
61
+ )
57
62
  end
58
63
  end
59
64
  end
@@ -24,9 +24,11 @@ module NattyUI
24
24
  # )
25
25
  # # => 'a' or 'b' or 'c' or nil if user aborted
26
26
  #
27
+ # @see NattyUI.in_stream
28
+ #
27
29
  # @param question [#to_s] Question to display
28
30
  # @param choices [#to_s] choices selectable via index (0..9)
29
- # @param result [Symbol] defines how the result ist returned
31
+ # @param result [Symbol] defines how the result will be returned
30
32
  # @param kw_choices [{Char => #to_s}] choices selectable with given char
31
33
  # @return [Char] when `result` is configured as `:char`
32
34
  # @return [#to_s] when `result` is configured as `:choice`
@@ -78,7 +80,7 @@ module NattyUI
78
80
  .to_h
79
81
  .merge!(kw_choices)
80
82
  .transform_keys! { |k| [k.to_s[0], ' '].max }
81
- .transform_values! { |v| v.to_s.tr("\r\n", ' ') }
83
+ .transform_values! { |v| v.to_s.tr("\r\n\t", ' ') }
82
84
  end
83
85
  end
84
86
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'element'
4
+
5
+ module NattyUI
6
+ module Features
7
+ # Request user input.
8
+ #
9
+ # @param question [#to_s] Question to display
10
+ # @return [String] the user input
11
+ # @return [nil] when input was aborted with `^C` or `^D`
12
+ def request(question) = _element(:Request, question)
13
+ end
14
+
15
+ class Wrapper
16
+ #
17
+ # An {Element} to request user input.
18
+ #
19
+ # @see Features#request
20
+ class Request < Element
21
+ protected
22
+
23
+ def _call(question)
24
+ NattyUI.readline(prompt(question), stream: wrapper.stream)
25
+ ensure
26
+ finish
27
+ end
28
+
29
+ def prompt(question) = "#{prefix}▶︎ #{question}: "
30
+ end
31
+ end
32
+ end
@@ -29,7 +29,7 @@ module NattyUI
29
29
 
30
30
  class Wrapper
31
31
  #
32
- # Visual element to keep text lines together.
32
+ # Visual {Element} to keep text lines together.
33
33
  #
34
34
  # A section can contain other elements and sections.
35
35
  #
@@ -38,23 +38,25 @@ module NattyUI
38
38
  class Section < Element
39
39
  # Close the section.
40
40
  #
41
- # @return [Section] itself when used without a code block
42
- # @return [nil] when used with a code block
41
+ # @return [Section] itself
43
42
  def close = _close(:closed)
44
43
 
45
44
  # Print given arguments as lines into the section.
45
+ # Optionally limit the line width to given `max_width`.
46
46
  #
47
- # @overload puts(...)
47
+ # @overload puts(..., max_width: nil)
48
48
  # @param [#to_s] ... objects to print
49
+ # @param [Integer, nil] max_width maximum line width
49
50
  # @comment @param [#to_s, nil] prefix line prefix
50
51
  # @comment @param [#to_s, nil] suffix line suffix
51
52
  # @return [Section] itself
52
- def puts(*args, prefix: nil, suffix: nil)
53
+ def puts(*args, max_width: nil, prefix: nil, suffix: nil)
53
54
  return self if @status
54
55
  @parent.puts(
55
56
  *args,
57
+ max_width: max_width,
56
58
  prefix: prefix ? "#{@prefix}#{prefix}" : @prefix,
57
- suffix: suffix ? "#{@suffix}#{suffix}" : @suffix
59
+ suffix: suffix ? "#{suffix}#{@suffix}" : @suffix
58
60
  )
59
61
  self
60
62
  end
@@ -66,7 +68,7 @@ module NattyUI
66
68
  # @return [Section] itself
67
69
  def space(lines = 1)
68
70
  @parent.puts(
69
- "\n" * ([lines.to_i, 1].max - 1),
71
+ "\n" * [lines.to_i, 1].max,
70
72
  prefix: @prefix,
71
73
  suffix: @suffix
72
74
  )
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'section'
4
- require_relative 'mixins'
4
+ require_relative '../mixins'
5
5
 
6
6
  module NattyUI
7
7
  module Features
@@ -11,7 +11,7 @@ module NattyUI
11
11
  # or {#failed}.
12
12
  #
13
13
  # @param (see #information)
14
- # @yieldparam [Wrapper::Task] section the created section
14
+ # @yieldparam [Wrapper::Task] task the created section
15
15
  # @return [Object] the result of the code block
16
16
  # @return [Wrapper::Task] itself, when no code block is given
17
17
  def task(title, *args, &block)
@@ -1,12 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'stringio'
3
+ require 'io/console'
4
4
  require_relative 'wrapper/ask'
5
5
  require_relative 'wrapper/framed'
6
6
  require_relative 'wrapper/heading'
7
+ require_relative 'wrapper/list_in_columns'
7
8
  require_relative 'wrapper/message'
8
9
  require_relative 'wrapper/progress'
9
10
  require_relative 'wrapper/query'
11
+ require_relative 'wrapper/request'
10
12
  require_relative 'wrapper/section'
11
13
  require_relative 'wrapper/task'
12
14
 
@@ -28,13 +30,13 @@ module NattyUI
28
30
  # @return [[Integer, Integer]] screen size as rows and columns
29
31
  def screen_size
30
32
  return @stream.winsize if @ws
31
- [ENV['LINES'].to_i.nonzero? || 25, ENV['COLUMNS'].to_i.nonzero? || 80]
33
+ [ENV['LINES'].to_i.nonzero? || 24, ENV['COLUMNS'].to_i.nonzero? || 80]
32
34
  end
33
35
 
34
36
  # @attribute [r] screen_rows
35
37
  # @return [Integer] number of screen rows
36
38
  def screen_rows
37
- @ws ? @stream.winsize[0] : (ENV['LINES'].to_i.nonzero? || 25)
39
+ @ws ? @stream.winsize[0] : (ENV['LINES'].to_i.nonzero? || 24)
38
40
  end
39
41
 
40
42
  # @attribute [r] screen_columns
@@ -46,24 +48,22 @@ module NattyUI
46
48
  # @!group Tool functions
47
49
 
48
50
  # Print given arguments as lines to the output stream.
51
+ # Optionally limit the line width to given `max_width`.
49
52
  #
50
- # @overload puts(...)
53
+ # @overload puts(..., max_width: nil)
51
54
  # @param [#to_s] ... objects to print
55
+ # @param [Integer, nil] max_width maximum line width
52
56
  # @comment @param [#to_s, nil] prefix line prefix
53
57
  # @comment @param [#to_s, nil] suffix line suffix
54
58
  # @return [Wrapper] itself
55
- def puts(*args, prefix: nil, suffix: nil)
59
+ def puts(*args, max_width: nil, prefix: nil, suffix: nil)
56
60
  if args.empty?
57
61
  @stream.puts(embellish("#{prefix}#{suffix}"))
58
62
  @lines_written += 1
59
63
  else
60
- StringIO.open do |io|
61
- io.puts(*args)
62
- io.rewind
63
- io.each(chomp: true) do |line|
64
- @stream.puts(embellish("#{prefix}#{line}#{suffix}"))
65
- @lines_written += 1
66
- end
64
+ NattyUI.each_line(*args, max_width: max_width) do |line|
65
+ @stream.puts(embellish("#{prefix}#{line}#{suffix}"))
66
+ @lines_written += 1
67
67
  end
68
68
  end
69
69
  @stream.flush
@@ -154,7 +154,7 @@ module NattyUI
154
154
  def initialize(stream)
155
155
  @stream = stream
156
156
  @lines_written = 0
157
- @ws = stream.respond_to?(:winsize) && stream.winsize&.size == 2
157
+ @ws = stream.respond_to?(:winsize) && stream.winsize&.all?(&:positive?)
158
158
  rescue Errno::ENOTTY
159
159
  @ws = false
160
160
  end
@@ -163,6 +163,12 @@ module NattyUI
163
163
  def prefix = nil
164
164
  alias suffix prefix
165
165
 
166
+ def prefix_width = 0
167
+ alias suffix_width prefix_width
168
+ alias width prefix_width
169
+
170
+ alias available_width screen_columns
171
+
166
172
  private_class_method :new
167
173
  end
168
174
  end