natty-ui 0.12.0 → 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 +23 -24
  4. data/examples/24bit-colors.rb +4 -9
  5. data/examples/3bit-colors.rb +28 -8
  6. data/examples/8bit-colors.rb +18 -23
  7. data/examples/attributes.rb +30 -25
  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 -18
  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 +47 -56
  36. data/examples/animate.rb +0 -44
  37. data/examples/attributes_list.rb +0 -14
  38. data/examples/demo.rb +0 -53
  39. data/examples/message.rb +0 -32
  40. data/examples/progress.rb +0 -68
  41. data/examples/query.rb +0 -41
  42. data/examples/read_key.rb +0 -13
  43. data/examples/table.rb +0 -41
  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 -521
  52. data/lib/natty-ui/ansi_wrapper.rb +0 -199
  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 -60
  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 -67
  70. data/lib/natty-ui/wrapper/progress.rb +0 -74
  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 -551
  76. data/lib/natty-ui/wrapper/task.rb +0 -55
  77. data/lib/natty-ui/wrapper.rb +0 -230
data/lib/natty-ui/ansi.rb DELETED
@@ -1,521 +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
- # aliases
378
- 'b' => 1, # bold
379
- '/b' => 22, # bold_off
380
- 'i' => 3, # italic
381
- '/i' => 23, # italic_off
382
- 'u' => 4, # underline
383
- '/u' => 24, # underline_off
384
- 'h' => 8, # hide
385
- '/h' => 28 # reveal
386
- }
387
- add = ->(s, n) { n.each_with_index { |a, idx| map[a] = s + idx } }
388
- add[
389
- 1,
390
- %w[
391
- bold
392
- faint
393
- italic
394
- underline
395
- blink
396
- rapid_blink
397
- invert
398
- hide
399
- strike
400
- primary_font
401
- font1
402
- font2
403
- font3
404
- font4
405
- font5
406
- font6
407
- font7
408
- font8
409
- font9
410
- fraktur
411
- double_underline
412
- bold_off
413
- italic_off
414
- underline_off
415
- blink_off
416
- proportional
417
- invert_off
418
- reveal
419
- strike_off
420
- ]
421
- ]
422
- add[
423
- 50,
424
- %w[
425
- proportional_off
426
- framed
427
- encircled
428
- overlined
429
- framed_off
430
- overlined_off
431
- ]
432
- ]
433
- add[73, %w[superscript subscript superscript_off]]
434
-
435
- map.merge!(
436
- map
437
- .filter_map do |name, att|
438
- if name.end_with?('_off')
439
- ["/#{name.delete_suffix('_off')}", att]
440
- end
441
- end
442
- .to_h
443
- )
444
- end
445
- end
446
- .to_hash
447
- .freeze
448
-
449
- CLR =
450
- Module
451
- .new do
452
- def self.to_hash
453
- clr = {
454
- 0 => 'black',
455
- 1 => 'red',
456
- 2 => 'green',
457
- 3 => 'yellow',
458
- 4 => 'blue',
459
- 5 => 'magenta',
460
- 6 => 'cyan',
461
- 7 => 'white'
462
- }
463
- map = {}
464
- add = ->(s, p) { clr.each_pair { |i, n| map["#{p}#{n}"] = s + i } }
465
- ul = ->(r, g, b) { "58;2;#{r};#{g};#{b}" }
466
- add[30, nil]
467
- map['default'] = 39
468
- add[90, 'bright_']
469
- add[30, 'fg_']
470
- map['fg_default'] = 39
471
- map['/fg'] = 39
472
- add[90, 'fg_bright_']
473
- add[40, 'bg_']
474
- map['bg_default'] = 49
475
- map['/bg'] = 49
476
- add[100, 'bg_bright_']
477
- add[40, 'on_']
478
- map['on_default'] = 49
479
- add[100, 'on_bright_']
480
- map.merge!(
481
- 'ul_black' => ul[0, 0, 0],
482
- 'ul_red' => ul[128, 0, 0],
483
- 'ul_green' => ul[0, 128, 0],
484
- 'ul_yellow' => ul[128, 128, 0],
485
- 'ul_blue' => ul[0, 0, 128],
486
- 'ul_magenta' => ul[128, 0, 128],
487
- 'ul_cyan' => ul[0, 128, 128],
488
- 'ul_white' => ul[128, 128, 128],
489
- 'ul_default' => '59',
490
- '/ul' => '59',
491
- 'ul_bright_black' => ul[64, 64, 64],
492
- 'ul_bright_red' => ul[255, 0, 0],
493
- 'ul_bright_green' => ul[0, 255, 0],
494
- 'ul_bright_yellow' => ul[255, 255, 0],
495
- 'ul_bright_blue' => ul[0, 0, 255],
496
- 'ul_bright_magenta' => ul[255, 0, 255],
497
- 'ul_bright_cyan' => ul[0, 255, 255],
498
- 'ul_bright_white' => ul[255, 255, 255]
499
- )
500
- end
501
- end
502
- .to_hash
503
- .freeze
504
-
505
- SATTR = ATTR.transform_keys(&:to_sym).compare_by_identity.freeze
506
- SCLR = CLR.transform_keys(&:to_sym).compare_by_identity.freeze
507
-
508
- private_constant(
509
- :ESC,
510
- :CLR_PREFIX,
511
- :PI2_THIRD,
512
- :PI4_THIRD,
513
- :SATTR,
514
- :CLR,
515
- :ATTR,
516
- :SCLR
517
- )
518
- end
519
- end
520
-
521
- require_relative 'ansi/constants'
@@ -1,199 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'wrapper'
4
-
5
- module NattyUI
6
- class AnsiWrapper < Wrapper
7
- def ansi? = true
8
-
9
- def puts(*args, **kwargs)
10
- return super if args.empty? || (animation = kwargs[:animation]).nil?
11
- animation = Animation[animation].new(wrapper, args, kwargs)
12
- @stream << Ansi::CURSOR_HIDE
13
- animation.perform(@stream)
14
- @lines_written += animation.lines_written
15
- (@stream << Ansi::CURSOR_SHOW).flush
16
- self
17
- end
18
-
19
- def page
20
- unless block_given?
21
- @stream.flush
22
- return self
23
- end
24
- (@stream << Ansi::SCREEN_BLANK).flush
25
- begin
26
- yield(self)
27
- ensure
28
- (@stream << Ansi::SCREEN_UNBLANK).flush
29
- end
30
- end
31
-
32
- def cls
33
- (@stream << Ansi::CLS).flush
34
- self
35
- end
36
-
37
- protected
38
-
39
- def pprint(strs, opts)
40
- prefix = opts[:prefix] and prefix = Text.embellish(prefix)
41
- suffix = opts[:suffix] and suffix = Text.embellish(suffix)
42
- return yield("#{prefix}#{suffix}") if strs.empty?
43
- max_width =
44
- opts.fetch(:max_width) do
45
- screen_columns - (opts[:prefix_width] || Text.width(prefix)) -
46
- (opts[:suffix_width] || Text.width(suffix))
47
- end
48
- case opts[:align]
49
- when :right
50
- Text.each_line(strs, max_width) do |line, width|
51
- width = max_width - width
52
- yield("#{prefix}#{' ' * width}#{line}#{suffix}")
53
- end
54
- when :center
55
- Text.each_line(strs, max_width) do |line, width|
56
- width = max_width - width
57
- right = width / 2
58
- yield(
59
- "#{prefix}#{' ' * (width - right)}#{line}#{' ' * right}#{suffix}"
60
- )
61
- end
62
- else
63
- Text.each_line(strs, max_width) { yield("#{prefix}#{_1}#{suffix}") }
64
- end
65
- end
66
-
67
- def temp_func
68
- count = @lines_written
69
- lambda do
70
- if (c = @lines_written - count).nonzero?
71
- @stream << Ansi.cursor_prev_line(c) << Ansi.screen_erase(:below) <<
72
- Ansi::CURSOR_FIRST_COLUMN
73
- @lines_written -= c
74
- end
75
- @stream.flush
76
- self
77
- end
78
- end
79
-
80
- class Progress < Progress
81
- def draw(title, spinner)
82
- @msg =
83
- "#{@parent.prefix}#{Ansi[:bold, 39]}➔#{Ansi[:reset, 39]} " \
84
- "#{title}#{Ansi::RESET} "
85
- (wrapper.stream << @msg << Ansi::CURSOR_HIDE).flush
86
- @msg = "#{Ansi::CLL}#{@msg}"
87
- return @msg << BAR_COLOR if @max_value
88
- @spinner = NattyUI::Spinner[spinner]
89
- end
90
-
91
- def redraw
92
- (wrapper.stream << @msg << (@max_value ? fullbar : @spinner.next)).flush
93
- end
94
-
95
- def end_draw = (wrapper.stream << Ansi::CLL << Ansi::CURSOR_SHOW).flush
96
-
97
- def fullbar
98
- percent = @value / @max_value
99
- count = (30 * percent).to_i
100
- mv = max_value.to_i.to_s
101
- "#{'█' * count}#{BAR_BACK}#{'▁' * (30 - count)}" \
102
- "#{BAR_INK} #{value.to_i.to_s.rjust(mv.size)}/#{mv} " \
103
- "(#{(percent * 100).round(2).to_s.rjust(6)})"
104
- end
105
-
106
- BAR_COLOR = Ansi[39, 295].freeze
107
- BAR_BACK = Ansi[236, 492].freeze
108
- BAR_INK = Ansi[:bold, 255, :on_default].freeze
109
- end
110
-
111
- module Temporary
112
- def temporary
113
- unless block_given?
114
- wrapper.stream.flush
115
- return self
116
- end
117
- count = wrapper.lines_written
118
- begin
119
- yield(self)
120
- ensure
121
- count = wrapper.lines_written - count
122
- if count.nonzero?
123
- wrapper.stream << Ansi.cursor_prev_line(count) <<
124
- Ansi.display(:erase_below)
125
- end
126
- wrapper.stream.flush
127
- end
128
- end
129
- end
130
-
131
- class Section < Section
132
- include Temporary
133
- end
134
-
135
- class Quote < Quote
136
- include Temporary
137
-
138
- def initialize(...)
139
- super
140
- @prefix = "#{Ansi[39]}#{@prefix}#{Ansi::RESET}"
141
- end
142
- end
143
-
144
- class Framed < Framed
145
- def color(str) = "#{Ansi[39]}#{str}#{Ansi::RESET}"
146
-
147
- def init(type)
148
- @prefix = "#{color(type[4])} "
149
- @suffix = "#{Ansi.cursor_column(parent.rcol)}#{color(type[4])}"
150
- aw = @parent.available_width - 2
151
- parent.puts(color("#{type[0]}#{type[5] * aw}#{type[1]}"))
152
- @finish = color("#{type[2]}#{type[5] * aw}#{type[3]}")
153
- end
154
- end
155
-
156
- class Message < Section
157
- protected
158
-
159
- def initialize(parent, title:, glyph:, **opts)
160
- color = COLORS[glyph] || COLORS[:default]
161
- glyph = NattyUI::Glyph[glyph]
162
- prefix_width = Text.width(glyph) + 1
163
- parent.puts(
164
- title,
165
- prefix: "#{glyph} #{color}",
166
- prefix_width: prefix_width,
167
- suffix: Ansi::RESET,
168
- suffix_width: 0
169
- )
170
- super(
171
- parent,
172
- prefix: ' ' * prefix_width,
173
- prefix_width: prefix_width,
174
- **opts
175
- )
176
- end
177
-
178
- white = Ansi[255]
179
- COLORS = {
180
- default: white,
181
- point: white,
182
- information: white,
183
- warning: Ansi[221],
184
- error: Ansi[208],
185
- completed: Ansi[82],
186
- failed: Ansi[196],
187
- task: Ansi[39],
188
- query: white
189
- }.compare_by_identity.freeze
190
- end
191
-
192
- class Task < Message
193
- include ProgressAttributes
194
- include TaskMethods
195
- end
196
- end
197
-
198
- private_constant :AnsiWrapper
199
- end