natty-ui 0.12.1 → 0.25.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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +22 -26
  4. data/examples/24bit-colors.rb +4 -7
  5. data/examples/3bit-colors.rb +28 -6
  6. data/examples/8bit-colors.rb +18 -21
  7. data/examples/attributes.rb +30 -22
  8. data/examples/cols.rb +40 -0
  9. data/examples/elements.rb +31 -0
  10. data/examples/examples.rb +45 -0
  11. data/examples/illustration.rb +56 -54
  12. data/examples/ls.rb +16 -16
  13. data/examples/named-colors.rb +23 -0
  14. data/examples/sections.rb +29 -0
  15. data/examples/tables.rb +62 -0
  16. data/examples/tasks.rb +52 -0
  17. data/lib/natty-ui/attributes.rb +604 -0
  18. data/lib/natty-ui/choice.rb +56 -0
  19. data/lib/natty-ui/dumb_choice.rb +45 -0
  20. data/lib/natty-ui/element.rb +78 -0
  21. data/lib/natty-ui/features.rb +798 -0
  22. data/lib/natty-ui/framed.rb +51 -0
  23. data/lib/natty-ui/ls_renderer.rb +93 -0
  24. data/lib/natty-ui/progress.rb +187 -0
  25. data/lib/natty-ui/section.rb +69 -0
  26. data/lib/natty-ui/table.rb +241 -0
  27. data/lib/natty-ui/table_renderer.rb +147 -0
  28. data/lib/natty-ui/task.rb +44 -0
  29. data/lib/natty-ui/temporary.rb +38 -0
  30. data/lib/natty-ui/theme.rb +303 -0
  31. data/lib/natty-ui/utils.rb +79 -0
  32. data/lib/natty-ui/version.rb +1 -1
  33. data/lib/natty-ui/width_finder.rb +125 -0
  34. data/lib/natty-ui.rb +89 -147
  35. metadata +44 -53
  36. data/examples/animate.rb +0 -42
  37. data/examples/attributes_list.rb +0 -12
  38. data/examples/demo.rb +0 -51
  39. data/examples/message.rb +0 -30
  40. data/examples/progress.rb +0 -66
  41. data/examples/query.rb +0 -39
  42. data/examples/read_key.rb +0 -13
  43. data/examples/table.rb +0 -39
  44. data/lib/natty-ui/animation/binary.rb +0 -36
  45. data/lib/natty-ui/animation/default.rb +0 -38
  46. data/lib/natty-ui/animation/matrix.rb +0 -51
  47. data/lib/natty-ui/animation/rainbow.rb +0 -28
  48. data/lib/natty-ui/animation/type_writer.rb +0 -44
  49. data/lib/natty-ui/animation.rb +0 -69
  50. data/lib/natty-ui/ansi/constants.rb +0 -75
  51. data/lib/natty-ui/ansi.rb +0 -530
  52. data/lib/natty-ui/ansi_wrapper.rb +0 -232
  53. data/lib/natty-ui/frame.rb +0 -53
  54. data/lib/natty-ui/glyph.rb +0 -64
  55. data/lib/natty-ui/key_map.rb +0 -142
  56. data/lib/natty-ui/preload.rb +0 -12
  57. data/lib/natty-ui/spinner.rb +0 -120
  58. data/lib/natty-ui/text/east_asian_width.rb +0 -2529
  59. data/lib/natty-ui/text.rb +0 -203
  60. data/lib/natty-ui/wrapper/animate.rb +0 -17
  61. data/lib/natty-ui/wrapper/ask.rb +0 -78
  62. data/lib/natty-ui/wrapper/element.rb +0 -79
  63. data/lib/natty-ui/wrapper/features.rb +0 -21
  64. data/lib/natty-ui/wrapper/framed.rb +0 -45
  65. data/lib/natty-ui/wrapper/heading.rb +0 -64
  66. data/lib/natty-ui/wrapper/horizontal_rule.rb +0 -37
  67. data/lib/natty-ui/wrapper/list_in_columns.rb +0 -138
  68. data/lib/natty-ui/wrapper/message.rb +0 -109
  69. data/lib/natty-ui/wrapper/mixins.rb +0 -75
  70. data/lib/natty-ui/wrapper/progress.rb +0 -63
  71. data/lib/natty-ui/wrapper/query.rb +0 -89
  72. data/lib/natty-ui/wrapper/quote.rb +0 -25
  73. data/lib/natty-ui/wrapper/request.rb +0 -54
  74. data/lib/natty-ui/wrapper/section.rb +0 -118
  75. data/lib/natty-ui/wrapper/table.rb +0 -550
  76. data/lib/natty-ui/wrapper/task.rb +0 -55
  77. data/lib/natty-ui/wrapper.rb +0 -239
data/lib/natty-ui/ansi.rb DELETED
@@ -1,530 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module NattyUI
4
- #
5
- # Helper module for ANSI escape codes.
6
- #
7
- module Ansi
8
- class << self
9
- # Supported attribute names.
10
- # @see []
11
- #
12
- # @attribute [r] attribute_names
13
- # @return [Array<Symbol>] supported attribute names
14
- def attribute_names = SATTR.keys.sort!
15
-
16
- # Supported color names.
17
- # @see []
18
- #
19
- # @attribute [r] color_names
20
- # @return [Array<Symbol>] supported color names
21
- def color_names = SCLR.keys
22
-
23
- # @!group Control functions
24
-
25
- # Move cursor given lines up.
26
- #
27
- # @param lines [Integer] number of lines to move
28
- # @return [String] ANSI control code
29
- def cursor_up(lines = 1) = "\e[#{lines}A"
30
-
31
- # Move cursor given lines down.
32
- #
33
- # @param (see cursor_up)
34
- # @return (see cursor_up)
35
- def cursor_down(lines = 1) = "\e[#{lines}B"
36
-
37
- # Move cursor given colums forward.
38
- #
39
- # @param columns [Integer] number of columns to move
40
- # @return (see cursor_up)
41
- def cursor_forward(columns = 1) = "\e[#{columns}C"
42
-
43
- # Move cursor given colums back.
44
- #
45
- # @param (see cursor_forward)
46
- # @return (see cursor_up)
47
- def cursor_back(columns = 1) = "\e[#{columns}D"
48
-
49
- # Move cursor of beginning of the given next line.
50
- #
51
- # @param (see cursor_up)
52
- # @return (see cursor_up)
53
- def cursor_next_line(lines = 1) = "\e[#{lines}E"
54
-
55
- # Move cursor of beginning of the given previous line.
56
- #
57
- # @param (see cursor_up)
58
- # @return (see cursor_up)
59
- def cursor_previous_line(lines = 1) = "\e[#{lines}F"
60
- alias cursor_prev_line cursor_previous_line
61
-
62
- # Move cursor to given column in the current row.
63
- #
64
- # @param column [Integer] column index
65
- # @return (see cursor_up)
66
- def cursor_column(column = 1) = "\e[#{column}G"
67
-
68
- # Move cursor to given row and column counting from the top left corner.
69
- #
70
- # @param row [Integer] row index
71
- # @param column [Integer] column index
72
- # @return (see cursor_up)
73
- def cursor_pos(row, column = nil)
74
- return column ? "\e[#{row};#{column}H" : "\e[#{row}H" if row
75
- column ? "\e[;#{column}H" : "\e[H"
76
- end
77
-
78
- # @comment ??? def cursor_tab(count = 1) = "\e[#{column}I"
79
-
80
- # Erase screen.
81
- #
82
- # @param part [:below, :above, :scrollback, :entire] part to erase
83
- # @return (see cursor_up)
84
- def screen_erase(part = :entire)
85
- return "\e[0J" if part == :below
86
- return "\e[1J" if part == :above
87
- return "\e[3J" if part == :scrollback
88
- "\e[2J"
89
- end
90
-
91
- # Clear part of current line.
92
- #
93
- # @param part [:to_end, :to_start, :entire] part to delete
94
- # @return (see cursor_up)
95
- def line_erase(part = :entire)
96
- return "\e[0K" if part == :to_end
97
- return "\e[1K" if part == :to_start
98
- "\e[2K"
99
- end
100
-
101
- # @comment ??? def line_insert(lines = 1) = "\e[#{lines}L"
102
- # @comment ??? def line_delete(lines = 1) = "\e[#{lines}M"
103
- # @comment ??? def chars_delete(count = 1) = "\e[#{count}P"
104
-
105
- # Scroll window given lines up.
106
- #
107
- # @param lines [Integer] number of lines to scroll
108
- # @return (see cursor_up)
109
- def scroll_up(lines = 1) = "\e[;#{lines}S"
110
-
111
- # Scroll window given lines down.
112
- #
113
- # @param (see scroll_up)
114
- # @return (see cursor_up)
115
- def scroll_down(lines = 1) = "\e[;#{lines}T"
116
-
117
- # @comment ??? def chars_erase(count = 1) = "\e[#{count}X"
118
- # @comment ??? def cursor_reverse_tab(count = 1) = "\e[#{count}Z"
119
-
120
- # set absolute col
121
- # @comment ??? doubled! def cursor_column(column = 1) = "\e[#{column}`"
122
- # set relative column
123
- # @comment ??? def cursor_column_rel(column = 1) = "\e[#{column}a"
124
- # set absolute row
125
- # @comment ??? def cursor_row(row = 1) = "\e[#{row}d"
126
- # set relative row
127
- # @comment ??? def cursor_row_rel(row = 1) = "\e[#{row}e"
128
-
129
- # Change window title.
130
- # This is not widely supported but by XTerm and iTerm.
131
- #
132
- # @param [String] title text
133
- # @return (see cursor_up)
134
- def window_title(title) = "\e]2;#{title}\e\\"
135
-
136
- # Change tab title.
137
- # This is not widely supported but by iTerm.
138
- #
139
- # @param [String] title text
140
- # @return (see cursor_up)
141
- def tab_title(title) = "\e]0;#{title}\a"
142
-
143
- # @!endgroup
144
-
145
- # @!group Tool functions
146
-
147
- # Combine given ANSI {.attribute_names}, {.color_names} and color codes.
148
- #
149
- # Colors can specified by their name for ANSI 3-bit and 4-bit colors.
150
- # For 8-bit ANSI colors use 2-digit hexadecimal values `00`...`ff`.
151
- #
152
- # To use RGB ANSI colors (24-bit colors) specify 3-digit or 6-digit
153
- # hexadecimal values `000`...`fff` or `000000`...`ffffff`.
154
- # This represent the `RRGGBB` values (or `RGB` for short version) like you
155
- # may known from CSS color notation.
156
- #
157
- # To use a color as background color prefix the color attribute with `bg_`
158
- # or `on_`.
159
- # To use a color as underline color prefix the color attribute with `ul_`.
160
- # To clarify that a color attribute have to be used as foreground
161
- # color use the prefix `fg_`.
162
- #
163
- # @example Valid Foreground Color Attributes
164
- # Ansi[:yellow]
165
- # Ansi['#fab']
166
- # Ansi['#00aa00']
167
- # Ansi[:fg_fab]
168
- # Ansi[:fg_00aa00]
169
- # Ansi[:af]
170
- # Ansi[:fg_af]
171
- #
172
- # @example Valid Background Color Attributes
173
- # Ansi[:bg_yellow]
174
- # Ansi[:bg_fab]
175
- # Ansi[:bg_00aa00]
176
- # Ansi['bg#00aa00']
177
- # Ansi[:bg_af]
178
- #
179
- # Ansi[:on_yellow]
180
- # Ansi[:on_fab]
181
- # Ansi[:on_00aa00]
182
- # Ansi['on#00aa00']
183
- # Ansi[:on_af]
184
- #
185
- # @example Valid Underline Color Attributes
186
- # Ansi[:underline, :ul_yellow]
187
- # Ansi[:underline, :ul_fab]
188
- # Ansi[:underline, :ul_00aa00]
189
- # Ansi[:underline, 'ul#00aa00']
190
- # Ansi[:underline, :ul_fa]
191
- # Ansi[:underline, :ul_bright_yellow]
192
- #
193
- # @example Combined attributes:
194
- # Ansi[:bold, :italic, :bright_white, :on_0000cc]
195
- #
196
- # @param attributes [Array<Symbol, String>] attribute names to be used
197
- # @return [String] combined ANSI attributes
198
- def [](*attributes)
199
- return '' if attributes.empty?
200
- "\e[#{
201
- attributes
202
- .map do |arg|
203
- case arg
204
- when Symbol
205
- SATTR[arg] || SCLR[arg] || color(arg.to_s) ||
206
- invalid_argument(arg)
207
- when String
208
- ATTR[arg] || CLR[arg] || color(arg) || invalid_argument(arg)
209
- when (0..255)
210
- "38;5;#{arg}"
211
- when (256..511)
212
- "48;5;#{arg - 256}"
213
- when (512..767)
214
- "58;5;#{arg - 512}"
215
- else
216
- invalid_argument(arg)
217
- end
218
- end
219
- .join(';')
220
- }m"
221
- end
222
-
223
- # Decorate given `str` with ANSI attributes and colors.
224
- #
225
- # @see []
226
- #
227
- # @param str [#to_s] object to be decorated
228
- # @param attributes [Symbol, String] attribute names to be used
229
- # @param reset [Boolean] whether to include reset code for ANSI attributes
230
- # @return [String] `str` converted and decorated with the ANSI `attributes`
231
- def embellish(str, *attributes, reset: true)
232
- attributes = self[*attributes]
233
- attributes.empty? ? str.to_s : "#{attributes}#{str}#{"\e[0m" if reset}"
234
- end
235
-
236
- # Remove ANSI functions, attributes and colors from given string.
237
- #
238
- # @see embellish
239
- #
240
- # @param str [#to_s] string to be modified
241
- # @return [String] string without ANSI attributes
242
- def blemish(str) = str.to_s.gsub(ESC, '')
243
-
244
- # Try to combine given ANSI attributes and colors.
245
- # The attributes and colors have to be seperated by space char (" ").
246
- #
247
- # @example Valid Attribute String
248
- # Ansi.try_convert('bold italic blink red on#00ff00')
249
- # # => ANSI attribute string for bold, italic text which blinks red on
250
- # # green background
251
- #
252
- # @example Invalid Attribute String
253
- # Ansi.try_convert('cool bold on green')
254
- # # => nil
255
- #
256
- # @param attributes [#to_s] attributes separated by space char (" ")
257
- # @return [String] combined ANSI attributes
258
- # @return [nil] when string does not contain valid attributes
259
- def try_convert(attributes)
260
- return if !attributes || (attributes = attributes.to_s.split).empty?
261
- "\e[#{
262
- attributes
263
- .map! { ATTR[_1] || CLR[_1] || color(_1) || return }
264
- .join(';')
265
- }m"
266
- end
267
-
268
- # @param str [#to_s] plain text string to enrich with color
269
- # @param type [:foreground, :background, :underline] type of coloring
270
- # @param frequence [Float] color change frequency
271
- # @param spread [Float] number of chars with same color
272
- # @param seed [Float] start index on sinus curve
273
- # @return [String] fancy text
274
- def rainbow(
275
- str,
276
- type: :foreground,
277
- frequence: 0.3,
278
- spread: 0.8,
279
- seed: 1.1
280
- )
281
- type = color_type(type)
282
- pos = -1
283
- str
284
- .to_s
285
- .chars
286
- .map! do |char|
287
- i = (seed + ((pos += 1) / spread)) * frequence
288
- "\e[#{type};2;#{(Math.sin(i) * 255).abs.to_i};" \
289
- "#{(Math.sin(i + PI2_THIRD) * 255).abs.to_i};" \
290
- "#{(Math.sin(i + PI4_THIRD) * 255).abs.to_i}m#{char}"
291
- end
292
- .join
293
- end
294
-
295
- # @!endgroup
296
-
297
- private
298
-
299
- def invalid_argument(name)
300
- raise(
301
- ArgumentError,
302
- "unknown Ansi attribute name - '#{name}'",
303
- caller(1)
304
- )
305
- end
306
-
307
- def color_type(type)
308
- case type
309
- when :background, :bg, 'background', 'bg'
310
- 48
311
- when :underline, :ul, 'underline', 'ul'
312
- 58
313
- else
314
- 38
315
- end
316
- end
317
-
318
- def color(val)
319
- base = CLR_PREFIX[val[0, 2]]
320
- idx = base ? 2 : 0
321
- base ||= '38'
322
- idx += 1 if val[idx] == '_' || val[idx] == ':'
323
- idx += 1 if val[idx] == '#'
324
- val = val[idx..]
325
- return "#{base};5;#{val.hex}" if /\A[[:xdigit:]]{1,2}\z/.match?(val)
326
- if /\A[[:xdigit:]]{6}\z/.match?(val)
327
- return "#{base};2;#{val[0, 2].hex};#{val[2, 2].hex};#{val[4, 2].hex}"
328
- end
329
- return unless /\A[[:xdigit:]]{3}\z/.match?(val)
330
- "#{base};2;#{(val[0] * 2).hex};#{(val[1] * 2).hex};#{(val[2] * 2).hex}"
331
- end
332
- end
333
-
334
- # @!visibility private
335
- CSI = /\e\[[\d;:]*[ABCDEFGHJKSTfminsuhl]/
336
-
337
- # @!visibility private
338
- OSC = /\e\]\d+(?:;[^;\a\e]+)*(?:\a|\e\\)/
339
-
340
- ESC = /(#{CSI})|(#{OSC})/
341
-
342
- CLR_PREFIX = {
343
- 'fg' => '38',
344
- 'bg' => '48',
345
- 'ul' => '58',
346
- 'on' => '48'
347
- }.freeze
348
-
349
- PI2_THIRD = 2 * Math::PI / 3
350
- PI4_THIRD = 4 * Math::PI / 3
351
-
352
- ATTR =
353
- Module
354
- .new do
355
- def self.to_hash
356
- map = {
357
- # alternative names
358
- 'reset' => '',
359
- 'slow_blink' => 5,
360
- 'conceal' => 8,
361
- 'default_font' => 10,
362
- 'doubly' => 21,
363
- 'faint_off' => 22,
364
- 'fraktur_off' => 23,
365
- 'spacing' => 26,
366
- 'conceal_off' => 28,
367
- 'spacing_off' => 50,
368
- 'encircled_off' => 54,
369
- 'subscript_off' => 75,
370
- # special
371
- 'curly_underline_off' => '4:0',
372
- 'dotted_underline_off' => '4:0',
373
- 'dashed_underline_off' => '4:0',
374
- 'curly_underline' => '4:3',
375
- 'dotted_underline' => '4:4',
376
- 'dashed_underline' => '4:5'
377
- }
378
- add = ->(s, n) { n.each_with_index { |a, idx| map[a] = s + idx } }
379
- add[
380
- 1,
381
- %w[
382
- bold
383
- faint
384
- italic
385
- underline
386
- blink
387
- rapid_blink
388
- invert
389
- hide
390
- strike
391
- primary_font
392
- font1
393
- font2
394
- font3
395
- font4
396
- font5
397
- font6
398
- font7
399
- font8
400
- font9
401
- fraktur
402
- double_underline
403
- bold_off
404
- italic_off
405
- underline_off
406
- blink_off
407
- proportional
408
- invert_off
409
- reveal
410
- strike_off
411
- ]
412
- ]
413
- add[
414
- 50,
415
- %w[
416
- proportional_off
417
- framed
418
- encircled
419
- overlined
420
- framed_off
421
- overlined_off
422
- ]
423
- ]
424
- add[73, %w[superscript subscript superscript_off]]
425
-
426
- add_alias =
427
- proc do |name, org_name|
428
- map[name] = map[org_name] or
429
- raise("undefined value - #{org_name}")
430
- map["/#{name}"] = map["#{org_name}_off"] or
431
- raise("undefined value - #{org_name}_off")
432
- end
433
- add_alias['b', 'bold']
434
- add_alias['i', 'italic']
435
- add_alias['u', 'underline']
436
- add_alias['bl', 'blink']
437
- add_alias['inv', 'invert']
438
- add_alias['s', 'strike']
439
- add_alias['sup', 'superscript']
440
- add_alias['sub', 'subscript']
441
- add_alias['prop', 'proportional']
442
- add_alias['sp', 'spacing']
443
-
444
- map.merge!(
445
- map
446
- .filter_map do |name, att|
447
- if name.end_with?('_off')
448
- ["/#{name.delete_suffix('_off')}", att]
449
- end
450
- end
451
- .to_h
452
- )
453
- end
454
- end
455
- .to_hash
456
- .freeze
457
-
458
- CLR =
459
- Module
460
- .new do
461
- def self.to_hash
462
- clr = {
463
- 0 => 'black',
464
- 1 => 'red',
465
- 2 => 'green',
466
- 3 => 'yellow',
467
- 4 => 'blue',
468
- 5 => 'magenta',
469
- 6 => 'cyan',
470
- 7 => 'white'
471
- }
472
- map = {}
473
- add = ->(s, p) { clr.each_pair { |i, n| map["#{p}#{n}"] = s + i } }
474
- ul = ->(r, g, b) { "58;2;#{r};#{g};#{b}" }
475
- add[30, nil]
476
- map['default'] = 39
477
- add[90, 'bright_']
478
- add[30, 'fg_']
479
- map['fg_default'] = 39
480
- map['/fg'] = 39
481
- add[90, 'fg_bright_']
482
- add[40, 'bg_']
483
- map['bg_default'] = 49
484
- map['/bg'] = 49
485
- add[100, 'bg_bright_']
486
- add[40, 'on_']
487
- map['on_default'] = 49
488
- add[100, 'on_bright_']
489
- map.merge!(
490
- 'ul_black' => ul[0, 0, 0],
491
- 'ul_red' => ul[128, 0, 0],
492
- 'ul_green' => ul[0, 128, 0],
493
- 'ul_yellow' => ul[128, 128, 0],
494
- 'ul_blue' => ul[0, 0, 128],
495
- 'ul_magenta' => ul[128, 0, 128],
496
- 'ul_cyan' => ul[0, 128, 128],
497
- 'ul_white' => ul[128, 128, 128],
498
- 'ul_default' => '59',
499
- '/ul' => '59',
500
- 'ul_bright_black' => ul[64, 64, 64],
501
- 'ul_bright_red' => ul[255, 0, 0],
502
- 'ul_bright_green' => ul[0, 255, 0],
503
- 'ul_bright_yellow' => ul[255, 255, 0],
504
- 'ul_bright_blue' => ul[0, 0, 255],
505
- 'ul_bright_magenta' => ul[255, 0, 255],
506
- 'ul_bright_cyan' => ul[0, 255, 255],
507
- 'ul_bright_white' => ul[255, 255, 255]
508
- )
509
- end
510
- end
511
- .to_hash
512
- .freeze
513
-
514
- SATTR = ATTR.transform_keys(&:to_sym).compare_by_identity.freeze
515
- SCLR = CLR.transform_keys(&:to_sym).compare_by_identity.freeze
516
-
517
- private_constant(
518
- :ESC,
519
- :CLR_PREFIX,
520
- :PI2_THIRD,
521
- :PI4_THIRD,
522
- :SATTR,
523
- :CLR,
524
- :ATTR,
525
- :SCLR
526
- )
527
- end
528
- end
529
-
530
- require_relative 'ansi/constants'