natty-ui 0.34.0 → 1.0.2

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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +0 -1
  3. data/README.md +6 -6
  4. data/examples/24bit-colors.rb +9 -5
  5. data/examples/3bit-colors.rb +7 -7
  6. data/examples/8bit-colors.rb +5 -5
  7. data/examples/attributes.rb +2 -3
  8. data/examples/elements.rb +9 -6
  9. data/examples/examples.rb +9 -9
  10. data/examples/frames.rb +31 -0
  11. data/examples/hbars.rb +6 -3
  12. data/examples/info.rb +13 -10
  13. data/examples/key-codes.rb +8 -9
  14. data/examples/ls.rb +24 -22
  15. data/examples/named-colors.rb +4 -3
  16. data/examples/sections.rb +27 -17
  17. data/examples/select.rb +28 -0
  18. data/examples/sh.rb +25 -7
  19. data/examples/tables.rb +19 -37
  20. data/examples/tasks.rb +32 -22
  21. data/examples/vbars.rb +5 -3
  22. data/lib/natty-ui/dumb_progress.rb +68 -0
  23. data/lib/natty-ui/element.rb +64 -65
  24. data/lib/natty-ui/features.rb +773 -872
  25. data/lib/natty-ui/frame.rb +87 -0
  26. data/lib/natty-ui/helper/table.rb +1376 -0
  27. data/lib/natty-ui/margin.rb +83 -0
  28. data/lib/natty-ui/progress.rb +116 -149
  29. data/lib/natty-ui/renderer/bars.rb +93 -0
  30. data/lib/natty-ui/renderer/choice.rb +56 -0
  31. data/lib/natty-ui/renderer/dumb_choice.rb +34 -0
  32. data/lib/natty-ui/renderer/dumb_select.rb +60 -0
  33. data/lib/natty-ui/renderer/dumb_shell_runner.rb +19 -0
  34. data/lib/natty-ui/renderer/heading.rb +26 -0
  35. data/lib/natty-ui/renderer/horizontal_rule.rb +32 -0
  36. data/lib/natty-ui/{ls_renderer.rb → renderer/ls.rb} +15 -27
  37. data/lib/natty-ui/renderer/mark.rb +13 -0
  38. data/lib/natty-ui/renderer/quote.rb +13 -0
  39. data/lib/natty-ui/renderer/select.rb +63 -0
  40. data/lib/natty-ui/renderer/shell.rb +15 -0
  41. data/lib/natty-ui/renderer/shell_runner.rb +29 -0
  42. data/lib/natty-ui/renderer/table_renderer.rb +429 -0
  43. data/lib/natty-ui/section.rb +142 -41
  44. data/lib/natty-ui/task.rb +39 -27
  45. data/lib/natty-ui/temporary.rb +27 -14
  46. data/lib/natty-ui/utils/border.rb +139 -0
  47. data/lib/natty-ui/utils/str_const.rb +62 -0
  48. data/lib/natty-ui/utils/utils.rb +47 -0
  49. data/lib/natty-ui/version.rb +1 -1
  50. data/lib/natty-ui.rb +87 -30
  51. metadata +31 -28
  52. data/examples/cols.rb +0 -38
  53. data/examples/illustration.rb +0 -60
  54. data/examples/options.rb +0 -28
  55. data/examples/themes.rb +0 -51
  56. data/lib/natty-ui/attributes.rb +0 -593
  57. data/lib/natty-ui/choice.rb +0 -67
  58. data/lib/natty-ui/dumb_choice.rb +0 -47
  59. data/lib/natty-ui/dumb_options.rb +0 -64
  60. data/lib/natty-ui/framed.rb +0 -51
  61. data/lib/natty-ui/hbars_renderer.rb +0 -66
  62. data/lib/natty-ui/options.rb +0 -78
  63. data/lib/natty-ui/shell_renderer.rb +0 -91
  64. data/lib/natty-ui/table.rb +0 -325
  65. data/lib/natty-ui/table_renderer.rb +0 -165
  66. data/lib/natty-ui/theme.rb +0 -403
  67. data/lib/natty-ui/utils.rb +0 -111
  68. data/lib/natty-ui/vbars_renderer.rb +0 -49
  69. data/lib/natty-ui/width_finder.rb +0 -137
  70. data/natty-ui.gemspec +0 -34
@@ -1,66 +1,167 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'element'
4
+ require_relative 'utils/border'
4
5
 
5
6
  module NattyUI
6
- # Display section used by
7
+ # An {Element} that draws a bordered container with an optional title.
7
8
  #
8
- # - {Features.section}
9
- # - {Features.message}
10
- # - {Features.information}
11
- # - {Features.warning}
12
- # - {Features.error}
13
- # - {Features.failed}
9
+ # Instances are created by {#section} and its typed shorthand variants:
10
+ # {#message}, {#information}, {#warning}, {#error}, and {#fatal}.
11
+ # The visual style (border colour, prefix mark) is determined by the `type:`
12
+ # argument.
14
13
  #
14
+ # All {Features} methods are available on this element.
15
+ #
16
+ # @example Via block (auto-close)
17
+ # ui.section 'Summary', type: :information do
18
+ # ui.puts '3 files processed.'
19
+ # end
20
+ #
21
+ # @example Manual close
22
+ # sec = ui.warning 'Disk space low'
23
+ # ui.puts 'Only 500 MB remaining.'
24
+ # sec.end
15
25
  class Section < Element
16
- include WithStatus
26
+ # @private
27
+ def puts(...) = @done == 0 ? super : self
28
+
29
+ # @private
30
+ def hr(kind = nil)
31
+ return self if @done != 0
32
+ width = @parent.columns
33
+ return _hr_border(width, @type.border) if kind.nil?
34
+ return _hr_border(width, Border[kind]) if kind.is_a?(Symbol)
35
+ kind = StrConst[kind]
36
+ return _hr_border(width, @type.border) if kind.width == 0
37
+ width -= 2
38
+ puts(
39
+ "#{@type.style}#{
40
+ if kind.width > width
41
+ kind.to_str[0, width]
42
+ elsif kind.width == width
43
+ kind
44
+ else
45
+ kind.to_str * (width / kind.width)
46
+ end
47
+ }"
48
+ )
49
+ end
17
50
 
18
51
  # @private
19
- def puts(*objects, **options) = @state ? self : super
52
+ def heading(level, title)
53
+ return self if @done != 0
54
+ width = @parent.columns
55
+ hr =
56
+ @type.border.horoe(
57
+ Border[level == 1 ? :double : :single],
58
+ width,
59
+ @type.style
60
+ )
61
+ @parent.puts(hr) if level < 3 && @start_line != NattyUI.lines_written
62
+ puts(title, padding: [0, 1])
63
+ @parent.puts(hr)
64
+ self
65
+ end
20
66
 
21
67
  private
22
68
 
23
- def _done(text)
24
- puts(*text) unless text.empty?
25
- @parent.puts(@border.bottom)
69
+ def done
70
+ @parent.puts(@type.bottom) if (@done += 1) == 1
26
71
  end
27
72
 
28
- def _failed
29
- @parent.puts(@border.bottom)
73
+ def _hr_border(width, border)
74
+ @parent.puts(@type.border.horoe(border, width, @type.style))
75
+ self
30
76
  end
31
77
 
32
- def show_title(title)
33
- return @parent.puts(@border.top) unless title
34
- prefix = @border.top_left
35
- suffix = @border.top_right
36
- @parent.puts(
37
- title,
38
- prefix: prefix,
39
- prefix_width: prefix.width,
40
- suffix: suffix,
41
- suffix_width: suffix.width
78
+ def initialize(parent, title, kind, border)
79
+ super(parent)
80
+ @type = Type[border, kind]
81
+ @prefix = @type.left
82
+ title = normalize(title) or return parent.puts(@type.top)
83
+ parent.puts(
84
+ "#{@type.title_left}#{title}#{@type.title_right}",
85
+ bbcode: false
42
86
  )
43
87
  end
44
88
 
45
- def initialize(parent, title, msg, kind)
46
- super(parent)
47
- title, rest = split(title) if title && !title.empty?
48
- @border = Theme.current.section_border(kind)
49
- show_title(title)
50
- @prefix = @border.prefix
51
- @prefix_width = @prefix.width
52
- puts(*rest) if rest && !rest.empty?
53
- puts(*msg) unless msg.empty?
89
+ def normalize(title)
90
+ return if title.nil? || title.empty?
91
+ title =
92
+ Text::Formatter[
93
+ title,
94
+ eol: false,
95
+ width:
96
+ @parent.columns - @type.title_left.width - @type.title_right.width
97
+ ].first
98
+ title.nil? || title.empty? ? nil : title
54
99
  end
55
100
 
56
- def split(title)
57
- rest =
58
- Text.each_line(
59
- title,
60
- limit: @parent.columns - 9,
61
- ansi: Terminal.ansi?
62
- ).to_a
63
- [rest.shift, rest]
101
+ # @private
102
+ class Type
103
+ def self.[](border, kind) = @defined.dig(border, kind)
104
+
105
+ attr_reader :border,
106
+ :style,
107
+ :top,
108
+ :title_left,
109
+ :title_right,
110
+ :left,
111
+ :bottom
112
+
113
+ private
114
+
115
+ def initialize(border, style, mark)
116
+ @border = border
117
+ @style = Ansi[*style]
118
+ @top =
119
+ StrConst[
120
+ "#{@style}#{border.top_left}#{border.top}#{border.extra}#{
121
+ Ansi::RESET
122
+ }"
123
+ ]
124
+ @title_left =
125
+ StrConst[
126
+ "#{@style}#{
127
+ border.top_left
128
+ }#{border.top}#{border.end}#{Ansi::RESET} #{mark}"
129
+ ]
130
+ @title_right =
131
+ StrConst[
132
+ "#{Ansi::RESET} #{@style}#{
133
+ border.begin
134
+ }#{border.top}#{border.extra}#{Ansi::RESET}"
135
+ ]
136
+ @left = StrConst["#{@style}#{border.left}#{Ansi::RESET} "]
137
+ @bottom =
138
+ StrConst[
139
+ "#{@style}#{border.bottom_left}#{
140
+ border.bottom
141
+ }#{border.extra}#{Ansi::RESET}"
142
+ ]
143
+ end
144
+
145
+ @kind = {
146
+ default: [:bright_blue, ''],
147
+ message: [:bright_blue, '[bright_blue]•[/fg] '],
148
+ information: [:bright_blue, '[bright_blue]𝒊[/fg] '],
149
+ warning: [:yellow, '[bright_yellow]![/fg] '],
150
+ error: [:red, '[bright_red]𝙓[/fg] '],
151
+ fatal: [:red, '[bright_red i]𝒊[/fg /i] ']
152
+ }.compare_by_identity
153
+ @kind.default = @kind[:default]
154
+
155
+ @defined =
156
+ Hash
157
+ .new do |h1, border|
158
+ h1[border] = Hash
159
+ .new { |h2, kind| h2[kind] = new(Border[border], *@kind[kind]) }
160
+ .compare_by_identity
161
+ end
162
+ .compare_by_identity
64
163
  end
164
+
165
+ private_constant :Type
65
166
  end
66
167
  end
data/lib/natty-ui/task.rb CHANGED
@@ -1,48 +1,60 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'element'
3
+ require_relative 'temporary'
4
4
 
5
5
  module NattyUI
6
- # Task display section used by {Features.task}.
6
+ # A {Temporary} element that represents a single labelled step.
7
7
  #
8
+ # Instances are created by {#task}. Any content printed inside
9
+ # a `Task` block disappears when the element is closed. Content printed
10
+ # with {#pin} is exempt — it is collected and reprinted after the
11
+ # erasure so it remains visible.
12
+ #
13
+ # All {Features} methods are available on this element, making it easy to
14
+ # nest sub-tasks or progress indicators inside a task block.
15
+ #
16
+ # @example Simple task with a block
17
+ # ui.task 'Installing dependencies' do
18
+ # run_install
19
+ # end
20
+ #
21
+ # @example Nested tasks
22
+ # ui.task 'Deploy' do
23
+ # ui.task('Build') { compile }
24
+ # ui.task('Upload') { upload }
25
+ # ui.pin 'Deployed to production', mark: :checkmark
26
+ # end
8
27
  class Task < Temporary
9
- include WithStatus
28
+ # @private
29
+ def fatal(...)
30
+ return self if (@done += 1) != 1
31
+ @start_line = nil
32
+ self.end
33
+ @parent.fatal(...)
34
+ end
10
35
 
11
36
  private
12
37
 
13
- def _done(text)
38
+ def done
39
+ return self if (@done += 1) != 1 || @start_line.nil?
14
40
  NattyUI.back_to_line(@start_line)
15
- @start_line = nil
16
- cm = Theme.current.mark(:checkmark)
17
41
  @parent.puts(
18
- *text,
19
- first_line_prefix: cm,
20
- first_line_prefix_width: cm.width,
42
+ @title,
43
+ prefix: @prefix,
44
+ cprefix: Utils.mark[:checkmark],
21
45
  pin: @pin
22
46
  )
23
- @pins&.each { |objects, options| puts(*objects, **options) }
47
+ @pins&.each { |objects, opts| @parent.puts(*objects, **opts) }
24
48
  end
25
49
 
26
- def _failed
27
- @start_line = nil
28
- end
29
-
30
- def initialize(parent, title, msg, pin)
50
+ def initialize(parent, title, pin)
31
51
  super(parent)
32
- @title = title
33
- @pin = pin
34
- style = Theme.current.task_style
35
- cm = Theme.current.mark(:current)
36
- @prefix = ' ' * cm.width
37
- @prefix_width = cm.width
38
52
  parent.puts(
39
- title,
40
- first_line_prefix: "#{cm}#{style}",
41
- first_line_prefix_width: cm.width,
42
- prefix: "#{@prefix}#{style}",
43
- prefix_width: cm.width
53
+ @title = title,
54
+ cprefix: Utils.mark[:current],
55
+ prefix: @prefix = Utils.mark[:none]
44
56
  )
45
- puts(*msg) unless msg.empty?
57
+ @pin = pin
46
58
  end
47
59
  end
48
60
  end
@@ -3,32 +3,45 @@
3
3
  require_relative 'element'
4
4
 
5
5
  module NattyUI
6
- # Temporary display section used by {Features.temporary}.
6
+ # An {Element} whose output is erased from the terminal when it is closed.
7
7
  #
8
+ # Instances are created by {#temporary}. Any content printed inside
9
+ # a `Temporary` block disappears when the element is closed.
10
+ # Content printed with {#pin} is exempt — it is collected and reprinted after
11
+ # the erasure so it remains visible.
12
+ #
13
+ # All {Features} methods are available on this element.
14
+ #
15
+ # @example Via block (auto-close)
16
+ # ui.temporary do
17
+ # ui.puts 'Thinking…'
18
+ # sleep 2
19
+ # end # "Thinking…" is erased here
20
+ #
21
+ # @example Manual close
22
+ # ui.temporary
23
+ # ui.puts 'Loading…'
24
+ # do_work
25
+ # ui.end # erases "Loading…"
8
26
  class Temporary < Element
9
27
  # @private
10
28
  def puts(*objects, **opts)
11
- return self if @state
12
- if opts.delete(:pin)
13
- (@pins ||= []) << [objects, opts.except(:prefix_width, :suffix_width)]
14
- end
29
+ return self if @done != 0
30
+ (@pins ||= []) << [objects, opts.dup] if opts.delete(:pin)
15
31
  super
16
32
  end
17
33
 
18
- # @private
34
+ private
35
+
19
36
  def done
20
- return self if @state
21
- NattyUI.back_to_line(@start_line) if @start_line
22
- @pins&.each { |objects, opts| puts(*objects, **opts) }
23
- @state = :ok
24
- self
37
+ return self if (@done += 1) != 1
38
+ NattyUI.back_to_line(@start_line)
39
+ @pins&.each { |objects, opts| @parent.puts(*objects, **opts) }
25
40
  end
26
41
 
27
- private
28
-
29
42
  def initialize(parent)
30
- @start_line = NattyUI.lines_written
31
43
  super
44
+ @start_line = NattyUI.lines_written
32
45
  end
33
46
  end
34
47
  end
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NattyUI
4
+ # @private
5
+ class Border
6
+ def self.defined = @defined.keys
7
+ def self.[](name) = @defined[name]
8
+ def self.connections(kind) = @connections[kind]
9
+
10
+ attr_reader :to_str, :extra
11
+
12
+ def left = @to_str[0]
13
+ def right = @to_str[0]
14
+ def top = @to_str[1]
15
+ def bottom = @to_str[1]
16
+ def top_left = @to_str[2]
17
+ def top_right = @to_str[3]
18
+ def bottom_left = @to_str[4]
19
+ def bottom_right = @to_str[5]
20
+
21
+ def vert_for(other) = @vert[select(other)]
22
+ def hor_for(other) = @hor[select(other)]
23
+ def inter_for(other) = @inter[select(other)]
24
+
25
+ def build(width, title, style)
26
+ return StrConst.empty, StrConst.empty if width < 3
27
+ elyts = style.empty? ? '' : Ansi::RESET
28
+ line = @to_str[1] * (width - 2)
29
+ if title && width > 8
30
+ title, size =
31
+ Text::Formatter[
32
+ title,
33
+ with_size: true,
34
+ eol: false,
35
+ width: width - 7
36
+ ].first
37
+ if size&.nonzero?
38
+ top =
39
+ "#{style}#{@to_str[2]}#{@to_str[1] * 2}#{elyts} #{
40
+ title
41
+ } #{style}#{@to_str[1] * (width - 6 - size)}#{@to_str[3]}#{elyts}"
42
+ end
43
+ end
44
+ [
45
+ lur = StrConst.new("#{style}#{@to_str[0]}#{elyts}", 1),
46
+ lur,
47
+ top || "#{style}#{@to_str[2]}#{line}#{@to_str[3]}#{elyts}",
48
+ "#{style}#{@to_str[4]}#{line}#{@to_str[5]}#{elyts}"
49
+ ]
50
+ end
51
+
52
+ def hor(other, width, style)
53
+ return if width < 3
54
+ hor = @hor[select(other)]
55
+ ret = "#{hor[1]}#{hor[0] * (width - 2)}#{hor[2]}"
56
+ style.empty? ? ret : "#{style}#{ret}#{Ansi::RESET}"
57
+ end
58
+
59
+ def horoe(other, width, style)
60
+ return if width < 2
61
+ hor = @hor[select(other)]
62
+ ret = "#{hor[1]}#{hor[0] * (width - 1)}"
63
+ style.empty? ? ret : "#{style}#{ret}#{Ansi::RESET}"
64
+ end
65
+
66
+ def vert(other, style)
67
+ vert = @vert[select(other)].chars
68
+ style.empty? ? vert : vert.map! { "#{style}#{it}#{Ansi::RESET}" }
69
+ end
70
+
71
+ def begin = (@begin ||= @hor[@to_str[1]][1])
72
+ def end = (@end ||= @hor[@to_str[1]][2])
73
+
74
+ def intersection(other, style)
75
+ ret = @inter[select(other)]
76
+ style.empty? ? ret : "#{style}#{ret}#{Ansi::RESET}"
77
+ end
78
+
79
+ private
80
+
81
+ def initialize(to_str)
82
+ @to_str = to_str
83
+ @vert, @hor, @inter, @extra = Border.connections(to_str[0])
84
+ end
85
+
86
+ def select(other)
87
+ case other
88
+ when Border
89
+ other.top
90
+ when Symbol
91
+ Border[other].top
92
+ when String
93
+ other[0]
94
+ else
95
+ @to_str[1]
96
+ end
97
+ end
98
+
99
+ def_hwd = ->(hs) do
100
+ hs.default = hs[hs.keys.first]
101
+ hs.freeze
102
+ end
103
+
104
+ @connections = {
105
+ '│' => [
106
+ def_hwd['─' => '│┬┴', '═' => '│╤╧', '━' => '│┯┷'],
107
+ def_hwd['─' => '─├┤', '═' => '═╞╡', '━' => '━┝┥'],
108
+ def_hwd['─' => '┼', '═' => '╪', '━' => '┿'],
109
+ '╴╴╴'
110
+ ],
111
+ '║' => [
112
+ def_hwd['═' => '║╦╩', '─' => '║╥╨'],
113
+ def_hwd['═' => '═╠╣', '─' => '─╟╢'],
114
+ def_hwd['═' => '╬', '─' => '╫'],
115
+ ' ═ '
116
+ ],
117
+ '┃' => [
118
+ def_hwd['━' => '┃┳┻', '─' => '┃┰┸'],
119
+ def_hwd['━' => '━┣┫', '─' => '─┠┨'],
120
+ def_hwd['━' => '╋', '─' => '╂'],
121
+ '╸╸╸'
122
+ ]
123
+ }
124
+
125
+ @defined = {
126
+ rounded: new('│─╭╮╰╯'),
127
+ single: new('│─┌┐└┘'),
128
+ double: new('║═╔╗╚╝'),
129
+ heavy: new('┃━┏┓┗┛'),
130
+ single_double: new('│═╒╕╘╛'),
131
+ double_single: new('║─╓╖╙╜'),
132
+ single_heavy: new('│━┍┑┕┙'),
133
+ heavy_single: new('┃─┎┒┖┚')
134
+ }.compare_by_identity
135
+ @defined.default = @defined[:rounded]
136
+ end
137
+
138
+ private_constant :Border
139
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NattyUI
4
+ # @private
5
+ class StrConst
6
+ class << self
7
+ attr_reader :empty
8
+
9
+ def from_str(str)
10
+ str, width = Text::Formatter.new(str, eol: false).lines_with_size[0]
11
+ width == 0 ? @empty : new(str, width)
12
+ end
13
+ alias [] from_str
14
+
15
+ def spacer(size = 1)
16
+ size < 1 ? @empty : new((' ' * size).freeze, size)
17
+ end
18
+
19
+ def join(*other)
20
+ other.map! { ::StrConst === it ? it : from_str(it) }
21
+ new(other.join.freeze, other.sum(&:width))
22
+ end
23
+ end
24
+
25
+ attr_reader :to_s, :width
26
+ def empty? = (@width == 0)
27
+ def inspect = "<#{@to_s.inspect}, width:#{@width}>"
28
+ alias to_str to_s
29
+
30
+ def ljust(width)
31
+ return '' if (space = width - @width) < 0
32
+ space == 0 ? @to_s : "#{@to_s}#{' ' * space}"
33
+ end
34
+
35
+ def rjust(width)
36
+ return '' if (space = width - @width) < 0
37
+ space == 0 ? @to_s : "#{' ' * space}#{@to_s}"
38
+ end
39
+
40
+ def +(other)
41
+ other = StrConst[other] unless StrConst === other
42
+ StrConst.new((@to_s + other.to_s).freeze, @width + other.width)
43
+ end
44
+
45
+ def initialize(str, width)
46
+ @to_s =
47
+ if str.frozen?
48
+ str.encoding == ENCODING ? str : str.encode(ENCODING).freeze
49
+ else
50
+ str = str.dup
51
+ str.encode!(ENCODING) if str.encoding != ENCODING
52
+ str.freeze
53
+ end
54
+ @width = width
55
+ end
56
+
57
+ ENCODING = Encoding::UTF_8
58
+ @empty = new('', 0)
59
+ end
60
+
61
+ private_constant :StrConst
62
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NattyUI
4
+ # @private
5
+ module Utils
6
+ class << self
7
+ attr_reader :mark
8
+
9
+ def affix(style)
10
+ return '', '' unless Terminal.ansi?
11
+ return '', '' if (style &&= Ansi[*style]).nil? || style.empty?
12
+ [style, Ansi::RESET]
13
+ end
14
+
15
+ def as_width(value, max)
16
+ return max if value == :auto
17
+ (Float === value ? (max * value).round : value.to_int).clamp(0, max)
18
+ end
19
+
20
+ def as_mark(mark)
21
+ case mark
22
+ when nil
23
+ @mark.default
24
+ when Symbol
25
+ @mark[mark]
26
+ else
27
+ "#{mark} "
28
+ end
29
+ end
30
+ end
31
+
32
+ @mark = {
33
+ default: StrConst['[bright_blue]•[/fg] '],
34
+ current: StrConst['[b bright_green]→ '],
35
+ active: StrConst['[b bright_green]→[/b /fg] '],
36
+ checkmark: StrConst['[b bright_green]✓[/b /fg] '],
37
+ item: StrConst['[d]•[/d] '],
38
+ current_item: StrConst['[b bright_green]●[/b /fg] '],
39
+ output: StrConst['[d]>[/d] '],
40
+ error: StrConst['[red]𝙓[/fg] ']
41
+ }.compare_by_identity
42
+ @mark.default = @mark[:default]
43
+ @mark[:none] = StrConst.spacer(@mark.default.width)
44
+ end
45
+
46
+ private_constant :Utils
47
+ end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module NattyUI
4
4
  # The version number of the gem.
5
- VERSION = '0.34.0'
5
+ VERSION = '1.0.2'
6
6
  end