natty-ui 0.7.0 → 0.9.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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +7 -3
  3. data/README.md +25 -47
  4. data/examples/24bit-colors.rb +27 -0
  5. data/examples/3bit-colors.rb +14 -0
  6. data/examples/8bit-colors.rb +31 -0
  7. data/examples/animate.rb +24 -0
  8. data/examples/attributes.rb +25 -159
  9. data/examples/demo.rb +53 -0
  10. data/examples/illustration.png +0 -0
  11. data/examples/illustration.rb +29 -0
  12. data/examples/{list_in_columns.rb → ls.rb} +13 -20
  13. data/examples/message.rb +30 -0
  14. data/examples/progress.rb +25 -30
  15. data/examples/query.rb +26 -28
  16. data/examples/read_key.rb +13 -0
  17. data/examples/table.rb +36 -0
  18. data/lib/natty-ui/ansi.rb +351 -305
  19. data/lib/natty-ui/ansi_constants.rb +73 -0
  20. data/lib/natty-ui/ansi_wrapper.rb +136 -162
  21. data/lib/natty-ui/features.rb +11 -18
  22. data/lib/natty-ui/key_map.rb +119 -0
  23. data/lib/natty-ui/line_animation/default.rb +35 -0
  24. data/lib/natty-ui/line_animation/matrix.rb +28 -0
  25. data/lib/natty-ui/line_animation/rainbow.rb +30 -0
  26. data/lib/natty-ui/line_animation/test.rb +29 -0
  27. data/lib/natty-ui/line_animation/type_writer.rb +64 -0
  28. data/lib/natty-ui/line_animation.rb +54 -0
  29. data/lib/natty-ui/version.rb +2 -2
  30. data/lib/natty-ui/wrapper/animate.rb +17 -0
  31. data/lib/natty-ui/wrapper/ask.rb +21 -21
  32. data/lib/natty-ui/wrapper/element.rb +19 -23
  33. data/lib/natty-ui/wrapper/framed.rb +29 -19
  34. data/lib/natty-ui/wrapper/heading.rb +26 -53
  35. data/lib/natty-ui/wrapper/horizontal_rule.rb +37 -0
  36. data/lib/natty-ui/wrapper/list_in_columns.rb +71 -12
  37. data/lib/natty-ui/wrapper/message.rb +20 -27
  38. data/lib/natty-ui/wrapper/progress.rb +40 -13
  39. data/lib/natty-ui/wrapper/query.rb +34 -31
  40. data/lib/natty-ui/wrapper/quote.rb +25 -0
  41. data/lib/natty-ui/wrapper/request.rb +21 -10
  42. data/lib/natty-ui/wrapper/section.rb +55 -39
  43. data/lib/natty-ui/wrapper/table.rb +298 -0
  44. data/lib/natty-ui/wrapper/task.rb +6 -7
  45. data/lib/natty-ui/wrapper.rb +123 -41
  46. data/lib/natty-ui.rb +65 -40
  47. metadata +28 -9
  48. data/examples/basic.rb +0 -62
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
@@ -28,6 +27,9 @@ module NattyUI
28
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)
@@ -90,23 +92,28 @@ module NattyUI
90
92
  end
91
93
  match.empty? or next "[[#{match}]]"
92
94
  reset = false
93
- Ansi.reset
95
+ Ansi::RESET
94
96
  end
95
- reset ? "#{ret}#{Ansi.reset}" : ret
97
+ reset ? "#{ret}#{Ansi::RESET}" : ret
96
98
  end
97
99
 
98
100
  # Remove embedded attribute descriptions from given string.
99
101
  #
100
102
  # @param [#to_s] str string to edit
101
- # @return ]String] edited string
102
- def plain(str)
103
- str
104
- .to_s
105
- .gsub(/(\[\[((?~\]\]))\]\])/) do
106
- match = Regexp.last_match[2]
107
- next match.empty? ? nil : "[[#{match}]]" if match.delete_prefix!('/')
108
- Ansi.try_convert(match) ? nil : "[[#{match}]]"
109
- end
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}]]"
115
+ end
116
+ ansi == :keep ? str : Ansi.blemish(str)
110
117
  end
111
118
 
112
119
  # Calculate monospace (display) width of given String.
@@ -115,7 +122,8 @@ module NattyUI
115
122
  # @param [#to_s] str string to calculate
116
123
  # @return [Integer] the display size
117
124
  def display_width(str)
118
- (str = str.to_s).empty? ? 0 : Reline::Unicode.calculate_width(str)
125
+ return 0 if (str = str.to_s).empty?
126
+ Reline::Unicode.calculate_width(plain(str), true)
119
127
  end
120
128
 
121
129
  # Convert given arguments into strings and yield each line.
@@ -133,7 +141,7 @@ module NattyUI
133
141
  def each_line(*strs, max_width: nil, &block)
134
142
  return to_enum(__method__, *strs, max_width: max_width) unless block
135
143
  if max_width.nil?
136
- strs.each { |str| str.to_s.each_line(chomp: true, &block) }
144
+ strs.each { _1.to_s.each_line(chomp: true, &block) }
137
145
  return nil
138
146
  end
139
147
  return if (max_width = max_width.to_i) <= 0
@@ -141,45 +149,51 @@ module NattyUI
141
149
  str
142
150
  .to_s
143
151
  .each_line(chomp: true) do |line|
144
- Reline::Unicode.split_by_width(line, max_width)[0].each do |part|
145
- yield(part) if part
146
- end
152
+ next yield(line) if line.empty?
153
+ lines, _height = Reline::Unicode.split_by_width(line, max_width)
154
+ lines.compact!
155
+ next if lines.empty?
156
+ lines.pop if lines[-1].empty?
157
+ lines.each(&block)
147
158
  end
148
159
  end
149
160
  nil
150
161
  end
151
162
 
152
- # Read user input line from {.in_stream}.
163
+ # Read next raw key (keyboard input) from {in_stream}.
153
164
  #
154
- # This method uses Ruby's Readline implementation (default gem). See there
155
- # for more information.
165
+ # The input will be returned as named key codes like "Ctrl+C" by default.
166
+ # This can be changed by the `mode` parameter:
156
167
  #
157
- # @see .valid_out?
168
+ # - `:named` - name if available (fallback to raw)
169
+ # - `:raw` - key code "as is"
170
+ # - `:both` - key code and name if available
158
171
  #
159
- # @param [#to_s] prompt input prompt
160
- # @param [false, nil, #call] completion disable autocompletion, use default
161
- # autocompletion or use given completion proc
162
- # @param [IO] stream writeable IO used to display output
163
- # @return [String] user input line
164
- # @return [nil] when user interrupted input with `^C` or `^D`
165
- def readline(prompt = nil, completion: false, stream: StdOut.stream)
166
- cp = Readline.completion_proc
167
- Readline.completion_proc = completion == false ? ->(*_) {} : completion
168
- Readline.output = stream
169
- Readline.input = @in_stream
170
- Readline.readline(prompt.to_s)
171
- rescue Interrupt
172
- stream.puts
172
+ # @param [:named, :raw, :both] mode modfies the result
173
+ # @return [String] read key
174
+ def read_key(mode: :named)
175
+ return @in_stream.getch unless defined?(@in_stream.getc)
176
+ return @in_stream.getc unless defined?(@in_stream.raw)
177
+ @in_stream.raw do |raw_stream|
178
+ key = raw_stream.getc
179
+ while (nc = raw_stream.read_nonblock(1, exception: false))
180
+ nc.is_a?(String) ? key += nc : break
181
+ end
182
+ return key if mode == :raw
183
+ return key, KEY_MAP[key]&.dup if mode == :both
184
+ KEY_MAP[key]&.dup || key
185
+ end
186
+ rescue Interrupt, SystemCallError
173
187
  nil
174
- ensure
175
- Readline.completion_proc = cp
176
188
  end
177
189
 
178
190
  private
179
191
 
180
192
  def wrapper_class(stream, ansi)
181
- return AnsiWrapper if ansi == true
182
- return Wrapper if ansi == false || ENV.key?('NO_COLOR')
193
+ return AnsiWrapper if ansi == true || ENV['ANSI'] == '1'
194
+ if ansi == false || ENV.key?('NO_COLOR') || ENV['TERM'] == 'dumb'
195
+ return Wrapper
196
+ end
183
197
  stream.tty? ? AnsiWrapper : Wrapper
184
198
  end
185
199
 
@@ -196,5 +210,16 @@ module NattyUI
196
210
  # Instance for standard error output.
197
211
  StdErr = stderr_is_stdout? ? StdOut : new(STDERR)
198
212
 
213
+ @element = StdOut
199
214
  self.in_stream = STDIN
215
+
216
+ autoload(:KEY_MAP, File.join(__dir__, 'natty-ui', 'key_map'))
217
+ private_constant :KEY_MAP
218
+ end
219
+
220
+ # @!visibility private
221
+ module Kernel
222
+ # @see NattyUI.element
223
+ # @return [NattyUI::Wrapper, NattyUI::Wrapper::Element] active UI element
224
+ def ui = NattyUI.element unless defined?(ui)
200
225
  end
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.7.0
4
+ version: 0.9.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-29 00:00:00.000000000 Z
11
+ date: 2024-07-07 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: []
@@ -26,29 +25,49 @@ files:
26
25
  - ".yardopts"
27
26
  - LICENSE
28
27
  - README.md
28
+ - examples/24bit-colors.rb
29
+ - examples/3bit-colors.rb
30
+ - examples/8bit-colors.rb
31
+ - examples/animate.rb
29
32
  - examples/attributes.rb
30
- - examples/basic.rb
33
+ - examples/demo.rb
31
34
  - examples/illustration.png
32
- - examples/list_in_columns.rb
35
+ - examples/illustration.rb
36
+ - examples/ls.rb
37
+ - examples/message.rb
33
38
  - examples/progress.rb
34
39
  - examples/query.rb
40
+ - examples/read_key.rb
41
+ - examples/table.rb
35
42
  - lib/natty-ui.rb
36
43
  - lib/natty-ui/ansi.rb
44
+ - lib/natty-ui/ansi_constants.rb
37
45
  - lib/natty-ui/ansi_wrapper.rb
38
46
  - lib/natty-ui/features.rb
47
+ - lib/natty-ui/key_map.rb
48
+ - lib/natty-ui/line_animation.rb
49
+ - lib/natty-ui/line_animation/default.rb
50
+ - lib/natty-ui/line_animation/matrix.rb
51
+ - lib/natty-ui/line_animation/rainbow.rb
52
+ - lib/natty-ui/line_animation/test.rb
53
+ - lib/natty-ui/line_animation/type_writer.rb
39
54
  - lib/natty-ui/version.rb
40
55
  - lib/natty-ui/wrapper.rb
56
+ - lib/natty-ui/wrapper/animate.rb
41
57
  - lib/natty-ui/wrapper/ask.rb
42
58
  - lib/natty-ui/wrapper/element.rb
43
59
  - lib/natty-ui/wrapper/framed.rb
44
60
  - lib/natty-ui/wrapper/heading.rb
61
+ - lib/natty-ui/wrapper/horizontal_rule.rb
45
62
  - lib/natty-ui/wrapper/list_in_columns.rb
46
63
  - lib/natty-ui/wrapper/message.rb
47
64
  - lib/natty-ui/wrapper/mixins.rb
48
65
  - lib/natty-ui/wrapper/progress.rb
49
66
  - lib/natty-ui/wrapper/query.rb
67
+ - lib/natty-ui/wrapper/quote.rb
50
68
  - lib/natty-ui/wrapper/request.rb
51
69
  - lib/natty-ui/wrapper/section.rb
70
+ - lib/natty-ui/wrapper/table.rb
52
71
  - lib/natty-ui/wrapper/task.rb
53
72
  - lib/natty_ui.rb
54
73
  homepage: https://github.com/mblumtritt/natty-ui
@@ -74,7 +93,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
74
93
  - !ruby/object:Gem::Version
75
94
  version: '0'
76
95
  requirements: []
77
- rubygems_version: 3.4.22
96
+ rubygems_version: 3.5.14
78
97
  signing_key:
79
98
  specification_version: 4
80
99
  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