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.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.md +23 -24
- data/examples/24bit-colors.rb +4 -9
- data/examples/3bit-colors.rb +28 -8
- data/examples/8bit-colors.rb +18 -23
- data/examples/attributes.rb +30 -25
- data/examples/cols.rb +40 -0
- data/examples/elements.rb +31 -0
- data/examples/examples.rb +45 -0
- data/examples/illustration.rb +56 -54
- data/examples/ls.rb +16 -18
- data/examples/named-colors.rb +23 -0
- data/examples/sections.rb +29 -0
- data/examples/tables.rb +62 -0
- data/examples/tasks.rb +52 -0
- data/lib/natty-ui/attributes.rb +604 -0
- data/lib/natty-ui/choice.rb +56 -0
- data/lib/natty-ui/dumb_choice.rb +45 -0
- data/lib/natty-ui/element.rb +78 -0
- data/lib/natty-ui/features.rb +798 -0
- data/lib/natty-ui/framed.rb +51 -0
- data/lib/natty-ui/ls_renderer.rb +93 -0
- data/lib/natty-ui/progress.rb +187 -0
- data/lib/natty-ui/section.rb +69 -0
- data/lib/natty-ui/table.rb +241 -0
- data/lib/natty-ui/table_renderer.rb +147 -0
- data/lib/natty-ui/task.rb +44 -0
- data/lib/natty-ui/temporary.rb +38 -0
- data/lib/natty-ui/theme.rb +303 -0
- data/lib/natty-ui/utils.rb +79 -0
- data/lib/natty-ui/version.rb +1 -1
- data/lib/natty-ui/width_finder.rb +125 -0
- data/lib/natty-ui.rb +89 -147
- metadata +47 -56
- data/examples/animate.rb +0 -44
- data/examples/attributes_list.rb +0 -14
- data/examples/demo.rb +0 -53
- data/examples/message.rb +0 -32
- data/examples/progress.rb +0 -68
- data/examples/query.rb +0 -41
- data/examples/read_key.rb +0 -13
- data/examples/table.rb +0 -41
- data/lib/natty-ui/animation/binary.rb +0 -36
- data/lib/natty-ui/animation/default.rb +0 -38
- data/lib/natty-ui/animation/matrix.rb +0 -51
- data/lib/natty-ui/animation/rainbow.rb +0 -28
- data/lib/natty-ui/animation/type_writer.rb +0 -44
- data/lib/natty-ui/animation.rb +0 -69
- data/lib/natty-ui/ansi/constants.rb +0 -75
- data/lib/natty-ui/ansi.rb +0 -521
- data/lib/natty-ui/ansi_wrapper.rb +0 -199
- data/lib/natty-ui/frame.rb +0 -53
- data/lib/natty-ui/glyph.rb +0 -64
- data/lib/natty-ui/key_map.rb +0 -142
- data/lib/natty-ui/preload.rb +0 -12
- data/lib/natty-ui/spinner.rb +0 -120
- data/lib/natty-ui/text/east_asian_width.rb +0 -2529
- data/lib/natty-ui/text.rb +0 -203
- data/lib/natty-ui/wrapper/animate.rb +0 -17
- data/lib/natty-ui/wrapper/ask.rb +0 -78
- data/lib/natty-ui/wrapper/element.rb +0 -79
- data/lib/natty-ui/wrapper/features.rb +0 -21
- data/lib/natty-ui/wrapper/framed.rb +0 -45
- data/lib/natty-ui/wrapper/heading.rb +0 -60
- data/lib/natty-ui/wrapper/horizontal_rule.rb +0 -37
- data/lib/natty-ui/wrapper/list_in_columns.rb +0 -138
- data/lib/natty-ui/wrapper/message.rb +0 -109
- data/lib/natty-ui/wrapper/mixins.rb +0 -67
- data/lib/natty-ui/wrapper/progress.rb +0 -74
- data/lib/natty-ui/wrapper/query.rb +0 -89
- data/lib/natty-ui/wrapper/quote.rb +0 -25
- data/lib/natty-ui/wrapper/request.rb +0 -54
- data/lib/natty-ui/wrapper/section.rb +0 -118
- data/lib/natty-ui/wrapper/table.rb +0 -551
- data/lib/natty-ui/wrapper/task.rb +0 -55
- 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
|