natty-ui 0.9.4 → 0.11.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/README.md +4 -11
- data/examples/24bit-colors.rb +8 -16
- data/examples/3bit-colors.rb +1 -1
- data/examples/8bit-colors.rb +15 -11
- data/examples/animate.rb +31 -11
- data/examples/attributes.rb +16 -14
- data/examples/attributes_list.rb +14 -0
- data/examples/demo.rb +4 -4
- data/examples/illustration.rb +52 -24
- data/examples/ls.rb +4 -4
- data/examples/message.rb +11 -9
- data/examples/progress.rb +2 -2
- data/examples/query.rb +1 -1
- data/examples/table.rb +23 -23
- data/lib/natty-ui/animation/binary.rb +37 -0
- data/lib/natty-ui/animation/default.rb +42 -0
- data/lib/natty-ui/animation/matrix.rb +55 -0
- data/lib/natty-ui/animation/rainbow.rb +28 -0
- data/lib/natty-ui/animation/type_writer.rb +44 -0
- data/lib/natty-ui/animation.rb +69 -0
- data/lib/natty-ui/ansi/constants.rb +1 -1
- data/lib/natty-ui/ansi.rb +46 -26
- data/lib/natty-ui/ansi_wrapper.rb +47 -51
- data/lib/natty-ui/key_map.rb +72 -49
- data/lib/natty-ui/preload.rb +1 -1
- data/lib/natty-ui/text.rb +75 -85
- data/lib/natty-ui/version.rb +1 -1
- data/lib/natty-ui/wrapper/animate.rb +1 -1
- data/lib/natty-ui/wrapper/ask.rb +1 -1
- data/lib/natty-ui/wrapper/element.rb +18 -10
- data/lib/natty-ui/wrapper/features.rb +0 -8
- data/lib/natty-ui/wrapper/framed.rb +7 -27
- data/lib/natty-ui/wrapper/message.rb +1 -1
- data/lib/natty-ui/wrapper/request.rb +1 -1
- data/lib/natty-ui/wrapper/section.rb +18 -18
- data/lib/natty-ui/wrapper/table.rb +432 -168
- data/lib/natty-ui/wrapper.rb +45 -48
- data/lib/natty-ui.rb +65 -2
- metadata +10 -8
- data/lib/natty-ui/line_animation/default.rb +0 -35
- data/lib/natty-ui/line_animation/matrix.rb +0 -28
- data/lib/natty-ui/line_animation/rainbow.rb +0 -30
- data/lib/natty-ui/line_animation/type_writer.rb +0 -44
- data/lib/natty-ui/line_animation.rb +0 -48
data/lib/natty-ui/text.rb
CHANGED
@@ -5,50 +5,41 @@ require_relative 'ansi'
|
|
5
5
|
module NattyUI
|
6
6
|
module Text
|
7
7
|
class << self
|
8
|
-
def plain(str) = Ansi.blemish(plain_but_ansi(str))
|
9
|
-
|
10
8
|
def plain_but_ansi(str)
|
11
9
|
(str = str.to_s).empty? and return str
|
12
|
-
str.gsub(
|
13
|
-
match = Regexp.last_match[
|
14
|
-
|
15
|
-
|
10
|
+
str.gsub(BBCODE) do
|
11
|
+
match = Regexp.last_match[1]
|
12
|
+
if match[0] == '/'
|
13
|
+
next if match.size == 1
|
14
|
+
next "[#{match[1..]}]" if match[1] == '/'
|
15
|
+
end
|
16
|
+
Ansi.try_convert(match) ? nil : "[#{match}]"
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
20
|
+
def plain(str) = Ansi.blemish(plain_but_ansi(str))
|
21
|
+
|
19
22
|
def embellish(str)
|
20
23
|
(str = str.to_s).empty? and return str
|
21
24
|
reset = false
|
22
25
|
str =
|
23
|
-
str.gsub(
|
24
|
-
match = Regexp.last_match[
|
26
|
+
str.gsub(BBCODE) do
|
27
|
+
match = Regexp.last_match[1]
|
25
28
|
if match[0] == '/'
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
+
if match.size == 1
|
30
|
+
reset = false
|
31
|
+
next Ansi::RESET
|
32
|
+
end
|
33
|
+
next "[#{match[1..]}]" if match[1] == '/'
|
29
34
|
end
|
35
|
+
|
30
36
|
ansi = Ansi.try_convert(match)
|
31
|
-
ansi ? reset = ansi : "[
|
37
|
+
ansi ? reset = ansi : "[#{match}]"
|
32
38
|
end
|
33
39
|
reset ? "#{str}#{Ansi::RESET}" : str
|
34
40
|
end
|
35
41
|
|
36
|
-
|
37
|
-
return 0 if (str = plain(str)).empty?
|
38
|
-
str = str.encode(UTF_8) if str.encoding != UTF_8
|
39
|
-
width = 0
|
40
|
-
in_zero_width = false
|
41
|
-
str.scan(WIDTH_SCANNER) do |np_start, np_end, _csi, _osc, gc|
|
42
|
-
if in_zero_width
|
43
|
-
in_zero_width = false if np_end
|
44
|
-
next
|
45
|
-
end
|
46
|
-
next in_zero_width = true if np_start
|
47
|
-
width += char_width(gc) if gc
|
48
|
-
end
|
49
|
-
width
|
50
|
-
end
|
51
|
-
|
42
|
+
# works for UTF-8 chars only!
|
52
43
|
def char_width(char)
|
53
44
|
ord = char.ord
|
54
45
|
return SPECIAL_CHARS[ord] || 2 if ord <= 0x1f
|
@@ -63,54 +54,67 @@ module NattyUI
|
|
63
54
|
size
|
64
55
|
end
|
65
56
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
57
|
+
def width(str)
|
58
|
+
return 0 if (str = plain_but_ansi(str)).empty?
|
59
|
+
str = str.encode(UTF_8) if str.encoding != UTF_8
|
60
|
+
width = 0
|
61
|
+
in_zero_width = false
|
62
|
+
str.scan(WIDTH_SCANNER) do |np_start, np_end, _csi, _osc, gc|
|
63
|
+
if in_zero_width
|
64
|
+
in_zero_width = false if np_end
|
65
|
+
next
|
66
|
+
end
|
67
|
+
next in_zero_width = true if np_start
|
68
|
+
width += char_width(gc) if gc
|
69
|
+
end
|
70
|
+
width
|
69
71
|
end
|
70
72
|
|
71
|
-
def
|
72
|
-
return if (max_width = max_width.to_i)
|
73
|
+
def each_line_plain(strs, max_width)
|
74
|
+
return if (max_width = max_width.to_i) < 1
|
73
75
|
strs.each do |str|
|
74
|
-
str
|
75
|
-
.
|
76
|
-
.
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
next seq << csi
|
92
|
-
end
|
93
|
-
next current << gc if in_zero_width
|
94
|
-
cw = char_width(gc)
|
95
|
-
if (width += cw) > max_width
|
96
|
-
yield(current)
|
97
|
-
width = cw
|
98
|
-
current = seq.dup
|
99
|
-
end
|
100
|
-
current << gc
|
76
|
+
plain_but_ansi(str).each_line(chomp: true) do |line|
|
77
|
+
next yield(line, 0) if line.empty?
|
78
|
+
empty = String.new(encoding: line.encoding)
|
79
|
+
current = empty.dup
|
80
|
+
width = 0
|
81
|
+
in_zero_width = false
|
82
|
+
line = line.encode(UTF_8) if line.encoding != UTF_8
|
83
|
+
line.scan(WIDTH_SCANNER) do |np_start, np_end, csi, osc, gc|
|
84
|
+
next in_zero_width = (current << "\1") if np_start
|
85
|
+
next in_zero_width = !(current << "\2") if np_end
|
86
|
+
next if osc || csi
|
87
|
+
next current << gc if in_zero_width
|
88
|
+
cw = char_width(gc)
|
89
|
+
if (width += cw) > max_width
|
90
|
+
yield(current, width - cw)
|
91
|
+
width = cw
|
92
|
+
current = empty.dup
|
101
93
|
end
|
102
|
-
|
94
|
+
current << gc
|
103
95
|
end
|
96
|
+
yield(current, width)
|
97
|
+
end
|
104
98
|
end
|
105
99
|
nil
|
106
100
|
end
|
107
101
|
|
108
|
-
def
|
109
|
-
|
102
|
+
def as_lines_plain(strs, width, height = nil)
|
103
|
+
ret = []
|
104
|
+
each_line_plain(strs, width) do |*info|
|
105
|
+
ret << info
|
106
|
+
break if height == ret.size
|
107
|
+
end
|
108
|
+
ret
|
109
|
+
end
|
110
|
+
|
111
|
+
def each_line(strs, max_width)
|
112
|
+
return if (max_width = max_width.to_i) < 1
|
110
113
|
strs.each do |str|
|
111
114
|
str
|
112
115
|
.to_s
|
113
116
|
.each_line(chomp: true) do |line|
|
117
|
+
line = embellish(line)
|
114
118
|
next yield(line, 0) if line.empty?
|
115
119
|
current = String.new(encoding: line.encoding)
|
116
120
|
seq = current.dup
|
@@ -123,8 +127,8 @@ module NattyUI
|
|
123
127
|
next (current << osc) && (seq << osc) if osc
|
124
128
|
if csi
|
125
129
|
current << csi
|
126
|
-
next if in_zero_width
|
127
130
|
next seq.clear if csi == "\e[m" || csi == "\e[0m"
|
131
|
+
next if in_zero_width
|
128
132
|
next seq << csi
|
129
133
|
end
|
130
134
|
next current << gc if in_zero_width
|
@@ -139,36 +143,21 @@ module NattyUI
|
|
139
143
|
yield(current, width)
|
140
144
|
end
|
141
145
|
end
|
142
|
-
nil
|
143
|
-
end
|
144
|
-
|
145
|
-
def as_lines(strs, max_width)
|
146
|
-
ret = []
|
147
|
-
each_line(strs, max_width) { ret << _1 }
|
148
|
-
ret
|
149
146
|
end
|
150
147
|
|
151
|
-
def
|
152
|
-
prefix = kwargs[:prefix] and prefix = prefix.empty? ? '' : cvt[prefix]
|
153
|
-
suffix = kwargs[:suffix] and suffix = suffix.empty? ? '' : cvt[suffix]
|
154
|
-
return ["#{prefix}#{suffix}"] if args.empty?
|
148
|
+
def as_lines(strs, width, height = nil)
|
155
149
|
ret = []
|
156
|
-
each_line(
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
kwargs.fetch(:prefix_width) { width(prefix) } -
|
161
|
-
kwargs.fetch(:suffix_width) { width(suffix) }
|
162
|
-
end
|
163
|
-
) { ret << "#{prefix}#{_1}#{suffix}" }
|
150
|
+
each_line(strs, width) do |*info|
|
151
|
+
ret << info
|
152
|
+
break if height == ret.size
|
153
|
+
end
|
164
154
|
ret
|
165
155
|
end
|
166
156
|
end
|
167
157
|
|
168
158
|
UTF_8 = Encoding::UTF_8
|
169
|
-
|
159
|
+
BBCODE = /(?:\[((?~[\[\]]))\])/
|
170
160
|
WIDTH_SCANNER = /\G(?:(\1)|(\2)|(#{Ansi::CSI})|(#{Ansi::OSC})|(\X))/
|
171
|
-
|
172
161
|
SPECIAL_CHARS = {
|
173
162
|
0x00 => 0,
|
174
163
|
0x01 => 1,
|
@@ -205,6 +194,7 @@ module NattyUI
|
|
205
194
|
}.compare_by_identity.freeze
|
206
195
|
|
207
196
|
autoload(:EastAsianWidth, File.join(__dir__, 'text', 'east_asian_width'))
|
197
|
+
private_constant :EastAsianWidth
|
208
198
|
|
209
199
|
@ambiguous_char_width = 1
|
210
200
|
end
|
data/lib/natty-ui/version.rb
CHANGED
@@ -6,7 +6,7 @@ module NattyUI
|
|
6
6
|
#
|
7
7
|
# @overload animate(..., animation: :default)
|
8
8
|
# @param [#to_s] ... objects to print
|
9
|
-
# @param [:default, :matrix, :rainbow, :type_writer]
|
9
|
+
# @param [:binary, :default, :matrix, :rainbow, :type_writer]
|
10
10
|
# animation type of animation
|
11
11
|
# @return [Wrapper::Section, Wrapper] it's parent object
|
12
12
|
def animate(*args, **kwargs)
|
data/lib/natty-ui/wrapper/ask.rb
CHANGED
@@ -5,25 +5,33 @@ require_relative 'features'
|
|
5
5
|
module NattyUI
|
6
6
|
class Wrapper
|
7
7
|
#
|
8
|
-
# Basic visual element
|
8
|
+
# Basic visual element.
|
9
9
|
#
|
10
10
|
class Element
|
11
11
|
include Features
|
12
12
|
|
13
|
-
# @
|
14
|
-
# @return [
|
15
|
-
|
16
|
-
|
17
|
-
# @return [Symbol] close status when closed
|
18
|
-
# @return [nil] when not closed
|
19
|
-
attr_reader :status
|
13
|
+
# @attribute [r] available_width
|
14
|
+
# @return [Integer] available columns count within the element
|
15
|
+
def available_width = @parent.available_width
|
20
16
|
|
21
17
|
# @attribute [r] closed?
|
22
18
|
# @return [Boolean] whether its closed or not
|
23
19
|
def closed? = (@status != nil)
|
24
20
|
|
25
|
-
# @return [
|
26
|
-
|
21
|
+
# @return [Section, Wrapper] parent element
|
22
|
+
attr_reader :parent
|
23
|
+
|
24
|
+
# @return [Symbol, nil] status when closed
|
25
|
+
attr_reader :status
|
26
|
+
|
27
|
+
# @attribute [r] wrapper
|
28
|
+
# @return [Wrapper] assigned output stream wrapper
|
29
|
+
def wrapper
|
30
|
+
return @wrapper if @wrapper
|
31
|
+
@wrapper = @parent
|
32
|
+
@wrapper = @wrapper.parent until @wrapper.is_a?(Wrapper)
|
33
|
+
@wrapper
|
34
|
+
end
|
27
35
|
|
28
36
|
# Close the element.
|
29
37
|
#
|
@@ -5,14 +5,6 @@ module NattyUI
|
|
5
5
|
# Features of {NattyUI} - methods to display natty elements.
|
6
6
|
#
|
7
7
|
module Features
|
8
|
-
# @return [Wrapper] assigned output stream wrapper
|
9
|
-
def wrapper
|
10
|
-
return @wrapper if @wrapper
|
11
|
-
@wrapper = @parent
|
12
|
-
@wrapper = @wrapper.parent until @wrapper.is_a?(Wrapper)
|
13
|
-
@wrapper
|
14
|
-
end
|
15
|
-
|
16
8
|
protected
|
17
9
|
|
18
10
|
def _element(type, ...)
|
@@ -11,12 +11,12 @@ module NattyUI
|
|
11
11
|
# {Wrapper::Element#close}.
|
12
12
|
#
|
13
13
|
# @param [Array<#to_s>] args more objects to print
|
14
|
-
# @param [:
|
14
|
+
# @param [:double, :heavy, :rounded, :semi, :simple] type frame type
|
15
15
|
# @yieldparam [Wrapper::Framed] framed the created section
|
16
16
|
# @return [Object] the result of the code block
|
17
17
|
# @return [Wrapper::Framed] itself, when no code block is given
|
18
18
|
def framed(*args, type: :rounded, &block)
|
19
|
-
_section(:Framed, args, type: type, &block)
|
19
|
+
_section(:Framed, args, type: NattyUI.frame(type), &block)
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
@@ -29,37 +29,17 @@ module NattyUI
|
|
29
29
|
protected
|
30
30
|
|
31
31
|
def initialize(parent, type:)
|
32
|
-
|
33
|
-
|
34
|
-
init(deco)
|
32
|
+
super(parent, prefix: "#{type[4]} ", prefix_width: 2, suffix_width: 2)
|
33
|
+
init(type)
|
35
34
|
end
|
36
35
|
|
37
|
-
def
|
38
|
-
if type.is_a?(Symbol)
|
39
|
-
ret = DECO[type] and return ret
|
40
|
-
elsif type.is_a?(String)
|
41
|
-
return type if type.size == 8
|
42
|
-
return type * 8 if type.size == 1
|
43
|
-
end
|
44
|
-
raise(ArgumentError, "invalid frame type - #{type.inspect}")
|
45
|
-
end
|
46
|
-
|
47
|
-
def init(deco)
|
36
|
+
def init(type)
|
48
37
|
aw = @parent.available_width - 1
|
49
|
-
parent.puts("#{
|
50
|
-
@finish = "#{
|
38
|
+
parent.puts("#{type[0]}#{type[5] * aw}")
|
39
|
+
@finish = "#{type[2]}#{type[5] * aw}"
|
51
40
|
end
|
52
41
|
|
53
42
|
def finish = @parent.puts(@finish)
|
54
|
-
|
55
|
-
DECO = {
|
56
|
-
rounded: '│╭─╮│╰─╯',
|
57
|
-
simple: '│┌─┐│└─┘',
|
58
|
-
heavy: '┃┏━┓┃┗━┛',
|
59
|
-
double: '║╔═╗║╚═╝',
|
60
|
-
semi: '│╒═╕│╘═╛',
|
61
|
-
block: '▌▛▀▜▐▙▄▟'
|
62
|
-
}.compare_by_identity.freeze
|
63
43
|
end
|
64
44
|
end
|
65
45
|
end
|
@@ -35,18 +35,18 @@ module NattyUI
|
|
35
35
|
|
36
36
|
# Print given arguments line-wise into the section.
|
37
37
|
#
|
38
|
-
# @
|
39
|
-
#
|
40
|
-
#
|
41
|
-
def puts(*args, **
|
38
|
+
# @param [#to_s] args objects to print
|
39
|
+
# @option options [:left, :right, :center] :align text alignment
|
40
|
+
# @return [Section] itself
|
41
|
+
def puts(*args, **options)
|
42
42
|
return self if @status
|
43
43
|
@parent.puts(
|
44
44
|
*args,
|
45
|
-
**
|
46
|
-
prefix: "#{@prefix}#{
|
47
|
-
prefix_width: @prefix_width +
|
48
|
-
suffix: "#{
|
49
|
-
suffix_width: @suffix_width +
|
45
|
+
**options.merge!(
|
46
|
+
prefix: "#{@prefix}#{options[:prefix]}",
|
47
|
+
prefix_width: @prefix_width + options[:prefix_width].to_i,
|
48
|
+
suffix: "#{options[:suffix]}#{@suffix}",
|
49
|
+
suffix_width: @suffix_width + options[:suffix_width].to_i
|
50
50
|
)
|
51
51
|
)
|
52
52
|
self
|
@@ -54,18 +54,18 @@ module NattyUI
|
|
54
54
|
|
55
55
|
# Print given arguments into the section.
|
56
56
|
#
|
57
|
-
# @
|
58
|
-
#
|
59
|
-
#
|
60
|
-
def print(*args, **
|
57
|
+
# @param [#to_s] args objects to print
|
58
|
+
# @option options [:left, :right, :center] :align text alignment
|
59
|
+
# @return [Section] itself
|
60
|
+
def print(*args, **options)
|
61
61
|
return self if @status
|
62
62
|
@parent.print(
|
63
63
|
*args,
|
64
|
-
**
|
65
|
-
prefix: "#{@prefix}#{
|
66
|
-
prefix_width: @prefix_width +
|
67
|
-
suffix: "#{
|
68
|
-
suffix_width: @suffix_width +
|
64
|
+
**options.merge!(
|
65
|
+
prefix: "#{@prefix}#{options[:prefix]}",
|
66
|
+
prefix_width: @prefix_width + options[:prefix_width].to_i,
|
67
|
+
suffix: "#{options[:suffix]}#{@suffix}",
|
68
|
+
suffix_width: @suffix_width + options[:suffix_width].to_i
|
69
69
|
)
|
70
70
|
)
|
71
71
|
self
|