natty-ui 0.6.0 → 0.8.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.
data/lib/natty-ui.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'readline'
4
3
  unless defined?(Reline)
5
4
  # load the Reline::Unicode part only
6
5
  # @!visibility private
@@ -15,7 +14,7 @@ require_relative 'natty-ui/ansi_wrapper'
15
14
 
16
15
  #
17
16
  # Module to create beautiful, nice, nifty, fancy, neat, pretty, cool, lovely,
18
- # natty user interfaces for your CLI.
17
+ # natty user interfaces for your CLI application.
19
18
  #
20
19
  # It creates {Wrapper} instances which can optionally support ANSI. The UI
21
20
  # consists of {Wrapper::Element}s and {Wrapper::Section}s for different
@@ -25,9 +24,12 @@ module NattyUI
25
24
  class << self
26
25
  # @see .valid_in?
27
26
  # @return [IO] IO stream used to read input
28
- # @raise TypeError when a non-readable stream will be assigned
27
+ # @raise [TypeError] when a non-readable stream will be assigned
29
28
  attr_reader :in_stream
30
29
 
30
+ # @return [Wrapper, Wrapper::Element] active UI element
31
+ attr_reader :element
32
+
31
33
  # @param [IO] stream to read input
32
34
  def in_stream=(stream)
33
35
  unless valid_in?(stream)
@@ -44,7 +46,7 @@ module NattyUI
44
46
  # @param [Boolean, :auto] ansi whether ANSI should be supported
45
47
  # or automatically selected
46
48
  # @return [Wrapper] wrapper for the given `stream`
47
- # @raise TypeError when `stream` is not a writable stream
49
+ # @raise [TypeError] when `stream` is not a writable stream
48
50
  def new(stream, ansi: :auto)
49
51
  unless valid_out?(stream)
50
52
  raise(TypeError, "writable IO required - #{stream.inspect}")
@@ -77,10 +79,9 @@ module NattyUI
77
79
  # Translate embedded attribute descriptions into ANSI control codes.
78
80
  #
79
81
  # @param [#to_s] str string to edit
80
- # @return ]String] edited string
82
+ # @return [String] edited string
81
83
  def embellish(str)
82
- str = str.to_s
83
- return +'' if str.empty?
84
+ return +'' if (str = str.to_s).empty?
84
85
  reset = false
85
86
  ret =
86
87
  str.gsub(/(\[\[((?~\]\]))\]\])/) do
@@ -91,26 +92,28 @@ module NattyUI
91
92
  end
92
93
  match.empty? or next "[[#{match}]]"
93
94
  reset = false
94
- Ansi.reset
95
+ Ansi::RESET
95
96
  end
96
- reset ? "#{ret}#{Ansi.reset}" : ret
97
+ reset ? "#{ret}#{Ansi::RESET}" : ret
97
98
  end
98
99
 
99
100
  # Remove embedded attribute descriptions from given string.
100
101
  #
101
102
  # @param [#to_s] str string to edit
102
- # @return ]String] edited string
103
- def plain(str)
104
- str
105
- .to_s
106
- .gsub(/(\[\[((?~\]\]))\]\])/) do
107
- match = Regexp.last_match[2]
108
- unless match.delete_prefix!('/')
109
- ansi = Ansi.try_convert(match)
110
- next ansi ? nil : "[[#{match}]]"
103
+ # @param [:keep,:remove] ansi keep or remove ANSI codes too
104
+ # @return [String] edited string
105
+ def plain(str, ansi: :keep)
106
+ str =
107
+ str
108
+ .to_s
109
+ .gsub(/(\[\[((?~\]\]))\]\])/) do
110
+ match = Regexp.last_match[2]
111
+ if match.delete_prefix!('/')
112
+ next match.empty? ? nil : "[[#{match}]]"
113
+ end
114
+ Ansi.try_convert(match) ? nil : "[[#{match}]]"
111
115
  end
112
- match.empty? ? nil : "[[#{match}]]"
113
- end
116
+ ansi == :keep ? str : Ansi.blemish(str)
114
117
  end
115
118
 
116
119
  # Calculate monospace (display) width of given String.
@@ -119,8 +122,8 @@ module NattyUI
119
122
  # @param [#to_s] str string to calculate
120
123
  # @return [Integer] the display size
121
124
  def display_width(str)
122
- str = str.to_s
123
- str.empty? ? 0 : Reline::Unicode.calculate_width(str)
125
+ return 0 if (str = str.to_s).empty?
126
+ Reline::Unicode.calculate_width(plain(str), true)
124
127
  end
125
128
 
126
129
  # Convert given arguments into strings and yield each line.
@@ -137,12 +140,11 @@ module NattyUI
137
140
  # @return [Enumerator] line enumerator
138
141
  def each_line(*strs, max_width: nil, &block)
139
142
  return to_enum(__method__, *strs, max_width: max_width) unless block
140
- unless max_width
141
- strs.each { |str| str.to_s.each_line(chomp: true, &block) }
143
+ if max_width.nil?
144
+ strs.each { _1.to_s.each_line(chomp: true, &block) }
142
145
  return nil
143
146
  end
144
- max_width = max_width.to_i
145
- return if max_width <= 0
147
+ return if (max_width = max_width.to_i) <= 0
146
148
  strs.each do |str|
147
149
  str
148
150
  .to_s
@@ -155,37 +157,28 @@ module NattyUI
155
157
  nil
156
158
  end
157
159
 
158
- # Read user input line from {.in_stream}.
160
+ # Read next raw key (keyboard input) from {in_stream}.
159
161
  #
160
- # This method uses Ruby's Readline implementation (default gem). See there
161
- # for more information.
162
- #
163
- # @see .valid_out?
164
- #
165
- # @param [#to_s] prompt input prompt
166
- # @param [false, nil, #call] completion disable autocompletion, use default
167
- # autocompletion or use given completion proc
168
- # @param [IO] stream writeable IO used to display output
169
- # @return [String] user input line
170
- # @return [nil] when user interrupted input with `^C` or `^D`
171
- def readline(prompt = nil, completion: false, stream: StdOut.stream)
172
- cp = Readline.completion_proc
173
- Readline.completion_proc = completion == false ? ->(*_) {} : completion
174
- Readline.output = stream
175
- Readline.input = @in_stream
176
- Readline.readline(prompt.to_s)
177
- rescue Interrupt
178
- stream.puts
179
- nil
180
- ensure
181
- Readline.completion_proc = cp
162
+ # @return [String] read key
163
+ def read_key
164
+ return @in_stream.getch unless defined?(@in_stream.getc)
165
+ return @in_stream.getc unless defined?(@in_stream.raw)
166
+ @in_stream.raw do |raw|
167
+ key = raw.getc
168
+ while (nc = raw.read_nonblock(1, exception: false))
169
+ nc.is_a?(String) ? key += nc : break
170
+ end
171
+ key
172
+ end
182
173
  end
183
174
 
184
175
  private
185
176
 
186
177
  def wrapper_class(stream, ansi)
187
178
  return AnsiWrapper if ansi == true
188
- return Wrapper if ansi == false || ENV.key?('NO_COLOR')
179
+ if ansi == false || ENV.key?('NO_COLOR') || ENV['TERM'] == 'dumb'
180
+ return Wrapper
181
+ end
189
182
  stream.tty? ? AnsiWrapper : Wrapper
190
183
  end
191
184
 
@@ -202,5 +195,10 @@ module NattyUI
202
195
  # Instance for standard error output.
203
196
  StdErr = stderr_is_stdout? ? StdOut : new(STDERR)
204
197
 
198
+ @element = StdOut
205
199
  self.in_stream = STDIN
206
200
  end
201
+
202
+ # @see NattyUI.element
203
+ # @return [NattyUI::Wrapper, NattyUI::Wrapper::Element] active UI element
204
+ def ui = NattyUI.element unless defined?(ui)
data/lib/natty_ui.rb ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'natty-ui'
metadata CHANGED
@@ -1,21 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: natty-ui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Blumtritt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-17 00:00:00.000000000 Z
11
+ date: 2024-06-18 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  This is the beautiful, nice, nifty, fancy, neat, pretty, cool, lovely,
15
- natty user interface you like to have for your command line interfaces
16
- (CLI).
17
- Here you find elegant, simple and beautiful tools that enhance your
18
- command line application functionally and aesthetically.
15
+ natty user interface tool you like to have for your command line applications.
16
+ It contains elegant, simple and beautiful features that enhance your
17
+ command line interfaces functionally and aesthetically.
19
18
  email:
20
19
  executables: []
21
20
  extensions: []
@@ -23,32 +22,42 @@ extra_rdoc_files:
23
22
  - README.md
24
23
  - LICENSE
25
24
  files:
25
+ - ".yardopts"
26
26
  - LICENSE
27
27
  - README.md
28
+ - examples/24bit-colors.rb
29
+ - examples/3bit-colors.rb
30
+ - examples/8bit-colors.rb
28
31
  - examples/attributes.rb
29
- - examples/basic.rb
32
+ - examples/demo.rb
30
33
  - examples/illustration.png
34
+ - examples/illustration.rb
31
35
  - examples/list_in_columns.rb
32
36
  - examples/progress.rb
33
37
  - examples/query.rb
38
+ - examples/table.rb
34
39
  - lib/natty-ui.rb
35
40
  - lib/natty-ui/ansi.rb
36
41
  - lib/natty-ui/ansi_wrapper.rb
37
42
  - lib/natty-ui/features.rb
38
- - lib/natty-ui/mixins.rb
39
43
  - lib/natty-ui/version.rb
40
44
  - lib/natty-ui/wrapper.rb
41
45
  - lib/natty-ui/wrapper/ask.rb
42
46
  - lib/natty-ui/wrapper/element.rb
43
47
  - lib/natty-ui/wrapper/framed.rb
44
48
  - lib/natty-ui/wrapper/heading.rb
49
+ - lib/natty-ui/wrapper/horizontal_rule.rb
45
50
  - lib/natty-ui/wrapper/list_in_columns.rb
46
51
  - lib/natty-ui/wrapper/message.rb
52
+ - lib/natty-ui/wrapper/mixins.rb
47
53
  - lib/natty-ui/wrapper/progress.rb
48
54
  - lib/natty-ui/wrapper/query.rb
55
+ - lib/natty-ui/wrapper/quote.rb
49
56
  - lib/natty-ui/wrapper/request.rb
50
57
  - lib/natty-ui/wrapper/section.rb
58
+ - lib/natty-ui/wrapper/table.rb
51
59
  - lib/natty-ui/wrapper/task.rb
60
+ - lib/natty_ui.rb
52
61
  homepage: https://github.com/mblumtritt/natty-ui
53
62
  licenses:
54
63
  - BSD-3-Clause
@@ -72,7 +81,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
72
81
  - !ruby/object:Gem::Version
73
82
  version: '0'
74
83
  requirements: []
75
- rubygems_version: 3.4.22
84
+ rubygems_version: 3.5.13
76
85
  signing_key:
77
86
  specification_version: 4
78
87
  summary: This is the beautiful, nice, nifty, fancy, neat, pretty, cool, lovely, natty
data/examples/basic.rb DELETED
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'natty-ui'
4
-
5
- UI = NattyUI::StdOut
6
-
7
- UI.space
8
-
9
- UI.h1 'NattyUI Basic Feature Demo', <<~TEXT
10
-
11
- This is a short demo of the basic features of [[75 bold]]NattyUI[[/]].
12
-
13
- TEXT
14
-
15
- UI.h2 'Feature: ANSI Colors and Attributes', <<~TEXT
16
-
17
- Like you might noticed you can [[57]]color [[d7]]text[[/]] for terminals supporting this
18
- feature. You can enforece the non-ANSI version by setting the environment
19
- variable [[75 italic]]NO_COLOR[[/]] to '[[75]]1[[/]]'. (see also [[underline]]https://no-color.org[[/]])
20
-
21
- You can not only color your text but also [[italic]]modify[[/]], [[underline]]decorate[[/]] and [[strike]]manipulate[[/]]
22
- it. The attributes are direct embedded into the text like '[[/bold red]]'
23
- and can be resetted with '[[//]]' or at the line end.
24
-
25
- TEXT
26
-
27
- UI.h2 'Feature: Sections' do |sec|
28
- sec.puts <<~TEXT
29
-
30
- Sections group text lines together and/or define their style. There are
31
- several section types which all can be stacked.
32
-
33
- Have a look at the different types of sections:
34
-
35
- TEXT
36
- sec.message 'Generic Message'
37
- sec.information 'Informational Message'
38
- sec.warning 'Warning Message'
39
- sec.error 'Error Message'
40
- sec.completed 'Completion Message'
41
- sec.failed 'Failure Message'
42
- sec.msg '[[d5]]Customized Message', symbol: '◉'
43
- sec.space
44
-
45
- sec.puts 'You can stack all kinds of sections together:'
46
- sec.space
47
- sec.framed('Rouned Frame') do |f1|
48
- f1.framed('Heavy Framed', type: :heavy) do |f2|
49
- f2.framed('Simple Frame', type: :simple) do |f3|
50
- f3.framed('Double Framed Section', type: :double) do |f4|
51
- f4.message(
52
- '[[fff400]]Frames are nice',
53
- "Just to show you that all sections\ncan be stacked...",
54
- symbol: '💛'
55
- )
56
- end
57
- end
58
- end
59
- end
60
- end
61
-
62
- UI.space