natty-ui 0.33.0 → 0.35.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 79e64cb2489dcf9efe4066e4f95252284f9227aafa4a77edd0d50f307b255cf2
4
- data.tar.gz: 7ec6f014b300740062e816fa9c787ff79b3fb7ce68e4b1c2d6967ae6eb58b53d
3
+ metadata.gz: f193a0c86adab949f0d0c40d522e879d47f0a75a6a52ae9cf4f9ff39a7582e0e
4
+ data.tar.gz: 2841fcf9135c48ef2df17ffcea4f5ff7619fe51dc62a489e58a9b7fc21f2bc1d
5
5
  SHA512:
6
- metadata.gz: 86abb66230c52bd8863aec983435b434e7272003a12db631cb0b46af9db938ffc16f3ba1f987dad53a10f9ff4393e5f4bf403d04b813bb68b4b189c5bf0b2860
7
- data.tar.gz: 34afc008cdec26384f7f20b7be6830a5be511147cfaef831d8e6869f858219ffeaea394ec8718ceb7513cfa3aaee19a852bb70d2b2ef641bc5880ca79d8de38d
6
+ metadata.gz: 8d956fcf0d7cf2809bda3db9e82e84a02fe260e7646c54f1ba195dc04e3e8842677d6bb2c6e3dc95712c2da59580d6753e5218cccd2779408d10c1174cf96d12
7
+ data.tar.gz: 99c7fa8acd5d4f6c12db79b95e02da4a269ade5439a8d6918ca8b47a07ac2dce24cb2f91a146f23fe3b9d4554588b2e6f9b02be476cf725d9e058985f7ed763a
data/.yardopts CHANGED
@@ -9,4 +9,3 @@
9
9
  lib/**/*.rb
10
10
  -
11
11
  README.md
12
- LICENSE
data/README.md CHANGED
@@ -4,7 +4,7 @@ This is the beautiful, nice, nifty, fancy, neat, pretty, cool, rich, lovely, nat
4
4
 
5
5
  - Gem: [rubygems.org](https://rubygems.org/gems/natty-ui)
6
6
  - Source: [codeberg.org](https://codeberg.org/mblumtritt/natty-ui)
7
- - Help: [rubydoc.info](https://rubydoc.info/gems/natty-ui/index)
7
+ - Help: [rubydoc.info](https://rubydoc.info/gems/natty-ui/NattyUI)
8
8
 
9
9
  ## Features
10
10
 
@@ -63,3 +63,7 @@ After that you only need one line of code to have everything together
63
63
  ```ruby
64
64
  require 'natty-ui'
65
65
  ```
66
+
67
+ ## Dependencies
68
+
69
+ It uses only the fantastic [Terminal.rb](https://rubygems.org/gems/terminal_rb) gem.
@@ -10,12 +10,14 @@ ui.message '[b]​ᓚᕠᗢ NattyUI[/b] [i green]8bit Color Support[/]' do
10
10
  [#ff]#{0.upto(7).map(&color).join}
11
11
  [#00]#{8.upto(15).map(&color).join}
12
12
  COLORS
13
+ ui.end
13
14
 
14
15
  ui.space
15
16
  ui.message 'Grayscale', <<~GRAYSCALE.chomp
16
17
  [#ff]#{0xe8.upto(0xf3).map(&color).join}
17
18
  [#ff]#{0xf4.upto(0xff).map(&color).join}
18
19
  GRAYSCALE
20
+ ui.end
19
21
 
20
22
  ui.space
21
23
  ui.message '6x6x6 Color Cube' do
data/examples/info.rb CHANGED
@@ -19,8 +19,8 @@ ui.message '[b]​ᓚᕠᗢ NattyUI[/b] [i green]Terminal Information[/]' do
19
19
  table.add 'Identifier', Terminal.application || 'unidentified'
20
20
  table.add 'ANSI support', Terminal.ansi? ? 'yes' : 'no'
21
21
  table.add 'Input mode', Terminal.input_mode
22
- table.add 'Colors', colors
23
22
  table.add 'Screen size', Terminal.size.join(' x ')
23
+ table.add 'Colors', colors
24
24
  table.columns[0].assign(width: 15, padding_right: 2, align: :right)
25
25
  table.columns[1].style = %i[bold bright_yellow]
26
26
  end
@@ -2,29 +2,24 @@
2
2
 
3
3
  require_relative '../lib/natty-ui'
4
4
 
5
- event = nil
6
- while true
7
- ui.temporary do
8
- ui.message '[b]​ᓚᕠᗢ NattyUI[/b] [i green]Keyboard Key Codes[/]' do
9
- ui.puts(
10
- "
11
- #{
12
- if event
13
- "#{event.to_a.size < 2 ? ' Key' : 'Keys'}: [bold bright_green]#{
14
- event.to_a.map { "[\\#{_1}]" }.join(' ')
15
- }[/]
16
- Code: [bright_blue]#{event.raw.inspect}[/]"
17
- else
18
- "Your terminal uses [bright_blue]#{
19
- Terminal.input_mode
20
- }[/] mode. Press a key!"
21
- end
22
- }
5
+ ui.message '[b]​ᓚᕠᗢ NattyUI[/b] [i green]Keyboard Key Codes[/]' do
6
+ ui.space
7
+ ui.puts(
8
+ "Your terminal uses [bright_blue]#{Terminal.input_mode}[/] mode. Press a key!",
9
+ '[dim](Exit with ESC)'
10
+ )
23
11
 
24
- [faint](Exit with ESC)"
25
- )
26
- end
27
- event = Terminal.read_key_event
28
- exit if event.nil? || event.name == 'Esc'
12
+ ui.space
13
+ # if you like to have mouse position changes reported then use
14
+ # 'mouse_move: true' in next line
15
+ Terminal.on_key_event(mouse: true, focus: true, mouse_move: false) do |event|
16
+ ui.puts(
17
+ "#{event.raw.inspect} [dim]→[/dim] [bold bright_green]#{
18
+ event.to_a.map { "[\\#{_1}]" }.join('+')
19
+ }#{" @ #{event.position.join(', ')}" if event.position}"
20
+ )
21
+ event.name != 'Esc'
29
22
  end
23
+
24
+ ui.space
30
25
  end
data/examples/sections.rb CHANGED
@@ -10,17 +10,25 @@ ui.message '[b]​ᓚᕠᗢ NattyUI[/b] [i green]Sections[/]' do
10
10
  means they may contain text, other sections, titles, horizontal rules,
11
11
  lists, progress bars and much more!
12
12
  TEXT
13
- end
14
13
 
15
- ui.space
16
- ui.warning "Warning\nThis is a warning message example."
17
-
18
- ui.space
19
- ui.error 'Error Message' do
20
14
  ui.space
21
15
  ui.cols do |cc|
22
16
  cc.add "()-()\n \\\"/\n  `", width: 6, style: :yellow
23
17
  cc.add 'You can add all other elements to a section.', vertical: :middle
24
18
  end
19
+
20
+ ui.framed(<<~TEXT, align: :centered, eol: false).end
21
+ Sections and messages can be called with code blocks and will be
22
+ automatically closed when the block ends. Without a code block the
23
+ section or message needs to be closed.
24
+ TEXT
25
25
  end
26
+
27
+ ui.warning "Warning\nThis is a warning message example."
28
+ ui.end
29
+
30
+ ui.error('Error Message', <<~MSG, align: :right, prefix: '[b red]>[/] ').end
31
+ Error message text with
32
+ print options.
33
+ MSG
26
34
  end
@@ -9,12 +9,12 @@ module NattyUI
9
9
  pin_line = NattyUI.lines_written
10
10
  draw
11
11
  last = @current
12
- while (event = Terminal.read_key_event)
12
+ Terminal.on_key_event do |event|
13
13
  case event.name
14
14
  when 'Esc', 'Ctrl+c'
15
- break nil if @abortable
15
+ return if @abortable
16
16
  when 'Enter', 'Space'
17
- break @ret[@current]
17
+ return @ret[@current]
18
18
  when 'Home'
19
19
  @current = 0
20
20
  when 'End'
@@ -24,11 +24,11 @@ module NattyUI
24
24
  when 'Down', 'Tab', 'k', 's'
25
25
  @current = 0 if (@current += 1) == @texts.size
26
26
  else
27
- next unless event.simple?
27
+ next true unless event.simple?
28
28
  c = event.key.ord
29
29
  @current = (c - 48).clamp(0, @texts.size - 1) if c.between?(48, 57)
30
30
  end
31
- next if last == @current
31
+ next true if last == @current
32
32
  pin_line = NattyUI.back_to_line(pin_line, erase: false)
33
33
  draw
34
34
  last = @current
@@ -7,15 +7,15 @@ module NattyUI
7
7
  def select
8
8
  yield(self) if block_given?
9
9
  draw
10
- while (event = Terminal.read_key_event)
10
+ Terminal.on_key_event do |event|
11
11
  return if @abortable && %w[Esc Ctrl+c].include?(event.name)
12
- next unless event.simple?
12
+ next true unless event.simple?
13
13
  code = event.raw.upcase
14
14
  if @ret.size <= 9
15
- next unless ('1'..'9').include?(code)
16
- code = @ret[code.ord - 49] and break code
15
+ next true unless ('1'..'9').include?(code)
16
+ code = @ret[code.ord - 49] and return code
17
17
  elsif ('A'..'Z').include?(code)
18
- code = @ret[code.ord - 65] and break code
18
+ code = @ret[code.ord - 65] and return code
19
19
  end
20
20
  end
21
21
  end
@@ -7,20 +7,20 @@ module NattyUI
7
7
  def select
8
8
  yield(self) if block_given?
9
9
  draw
10
- while (event = Terminal.read_key_event)
10
+ Terminal.on_key_event do |event|
11
11
  return if @abortable && %w[Esc Ctrl+c].include?(event.name)
12
12
  return @opts.transform_values(&:last) if event.name == 'Enter'
13
- next unless event.simple?
13
+ next true unless event.simple?
14
14
  code = event.raw.upcase
15
15
  if @opts.size <= 9
16
- next unless ('1'..'9').include?(code)
16
+ next true unless ('1'..'9').include?(code)
17
17
  offset = 49
18
18
  elsif ('A'..'Z').include?(code)
19
19
  offset = 65
20
20
  else
21
- next
21
+ next true
22
22
  end
23
- key = @opts.keys[code.ord - offset] or next
23
+ key = @opts.keys[code.ord - offset] or next true
24
24
  @opts[key][-1] = !@opts[key][-1]
25
25
  @parent.space
26
26
  draw
@@ -8,6 +8,11 @@ module NattyUI
8
8
  class Element
9
9
  include Features
10
10
 
11
+ # Close the element.
12
+ #
13
+ # @return [Features] current element
14
+ def end = NattyUI.__send__(:_end, self)
15
+
11
16
  # @private
12
17
  def columns = @parent.columns - @prefix_width - @suffix_width
13
18
 
@@ -71,13 +76,13 @@ module NattyUI
71
76
  # Close the element.
72
77
  #
73
78
  # @param [#to_s] text optional message
74
- # @return [Element] itself
79
+ # @return [Features] current element
75
80
  def ok(*text)
76
81
  return self if @state
77
82
  text = [@title] if text.empty? && @title
78
83
  _done(text)
79
84
  @state = :ok
80
- self
85
+ self.end
81
86
  end
82
87
  alias done ok
83
88
 
@@ -86,14 +91,17 @@ module NattyUI
86
91
  # @param text [#to_s] optional message
87
92
  # @yield optionally, when block is given
88
93
  # @yieldparam failed [Section] see {Features.failed}
89
- # @return [Element] itself
94
+ # @return [Features] current element
90
95
  def failed(*text, &block)
91
96
  return self if @state
92
- _failed
97
+ if text.empty?
98
+ super(@title, &block).ok if @title
99
+ else
100
+ super.ok
101
+ end
93
102
  @state = :failed
94
- text = [@title] if text.empty? && @title
95
- @parent.failed(*text, &block)
96
- self
103
+ _failed
104
+ self.end
97
105
  end
98
106
  end
99
107
  end
@@ -2,7 +2,8 @@
2
2
 
3
3
  module NattyUI
4
4
  # These are all supported features by {NattyUI} or any other sub- element
5
- # like {section}, {message}, {task}, ...
5
+ # like {#section}, {#message}, {#information}, {#warning}, {#error},
6
+ # {#failed}, {#framed}, {#task}, ...
6
7
  #
7
8
  # Any printed text can contain *BBCode*-like embedded ANSI attributes which
8
9
  # will be used when the output terminal supports attributes and colors.
@@ -26,8 +27,8 @@ module NattyUI
26
27
  #
27
28
  # @see #pin
28
29
  #
29
- # @param text [#to_s]
30
- # one or more convertible objects to print line by line
30
+ # @param text [*#to_s]
31
+ # one or more objects to print line by line
31
32
  # @param options [{Symbol => Object}]
32
33
  # @option options [:left, :right, :centered] :align (:left)
33
34
  # text alignment
@@ -45,7 +46,7 @@ module NattyUI
45
46
  ignore_newline = options[:eol] == false || options[:ignore_newline]
46
47
 
47
48
  if (max_width = options[:max_width]).nil?
48
- return self if (max_width = Terminal.columns).zero?
49
+ max_width = Terminal.columns
49
50
  elsif max_width < 1
50
51
  if max_width > 0
51
52
  max_width *= Terminal.columns
@@ -56,6 +57,8 @@ module NattyUI
56
57
  end
57
58
  end
58
59
 
60
+ return self if max_width <= 0
61
+
59
62
  prefix_width =
60
63
  if (prefix = options[:prefix])
61
64
  prefix = Ansi.bbcode(prefix) if bbcode
@@ -117,8 +120,7 @@ module NattyUI
117
120
  end
118
121
 
119
122
  unless options[:expand]
120
- lines = lines.to_a
121
- max_width = lines.max_by(&:last).last
123
+ max_width = (lines = lines.to_a).max_by(&:last)[-1]
122
124
  end
123
125
 
124
126
  case align
@@ -569,13 +571,14 @@ module NattyUI
569
571
  # @return [Object]
570
572
  # the result of the given block
571
573
  def progress(title, max: nil, pin: false, &block)
572
- progress =
574
+ __with(
573
575
  if Terminal.ansi?
574
576
  Progress.new(self, title, max, pin)
575
577
  else
576
578
  DumbProgress.new(self, title, max)
577
- end
578
- block ? __with(progress, &block) : progress
579
+ end,
580
+ &block
581
+ )
579
582
  end
580
583
 
581
584
  # Execute a program.
@@ -648,101 +651,163 @@ module NattyUI
648
651
  #
649
652
 
650
653
  # Create a visually separated section for the output of text elements.
654
+ # Besides this simple sections there exist sections of different kinds:
655
+ # {#message}, {#information}, {#warning}, {#error}, {#failed}, {#framed}
656
+ #
651
657
  # Like any other {Element} sections support all {Features}.
652
658
  #
653
- # @example
654
- # ui.section do |section|
659
+ # @overload section#
660
+ # @example
661
+ # ui.section do |section|
662
+ # section.h1 'About Sections'
663
+ # section.space
664
+ # section.puts 'Sections are areas of text elements.'
665
+ # section.puts 'You can use any other feature inside such an area.'
666
+ # end
667
+ # # => ╭────╶╶╶
668
+ # # => │ ╴╶╴╶─═══ About Sections ═══─╴╶╴╶
669
+ # # => │
670
+ # # => │ Sections are areas of text elements.
671
+ # # => │ You can use any other feature inside such an area.
672
+ # # => ╰──── ─╶╶╶
673
+ #
674
+ # @yieldparam section [Section]
675
+ # itself
676
+ # @return [Object]
677
+ # the result of the given block
678
+ #
679
+ # @overload section(*text, **options)
680
+ #
681
+ # @example
682
+ # section = ui.section
655
683
  # section.h1 'About Sections'
656
684
  # section.space
657
685
  # section.puts 'Sections are areas of text elements.'
658
- # section.puts 'You can use any other feature inside such an area.'
659
- # end
660
- # # => ╭────╶╶╶
661
- # # => │ ╴╶╴╶─═══ About Sections ═══─╴╶╴╶
662
- # # => │
663
- # # => │ Sections are areas of text elements.
664
- # # => │ You can use any other feature inside such an area.
665
- # # => ╰──── ─╶╶╶
666
- #
667
- # @param text [#to_s]
668
- # convertible objects to print line by line
669
- #
670
- # @yieldparam section [Section]
671
- # itself
686
+ # section.end # close the section
672
687
  #
673
- # @return [Object]
674
- # the result of the given block
675
- def section(*text, &block) = __sec(:default, nil, text, &block)
676
-
677
- # @!macro like_msg
678
- # @see section
679
- # @param title [#to_s]
680
- # title to print as section head
681
- # @param text (see section)
682
- # @yieldparam (see section)
683
- # @return (see section)
684
-
685
- # Create a visually separated section with a title for the output of text
688
+ # @param text [*#to_s]
689
+ # optional objects to print line by line
690
+ # @param options [{Symbol => Object}]
691
+ # print options – see {puts}
692
+ # @return [Section]
693
+ # itself
694
+ def section(*text, **options, &block)
695
+ __sec(:default, false, nil, text, options, &block)
696
+ end
697
+ alias begin section
698
+
699
+ # @!macro msg_like
700
+ #
701
+ # Like any other {Element} sections support all {Features}.
702
+ #
703
+ # @overload $0(title)
704
+ # @param title [#to_s]
705
+ # section title
706
+ # @yieldparam message [Section]
707
+ # itself
708
+ # @return [Object]
709
+ # the result of the given block
710
+ # @overload $0(title, *text, **options)
711
+ # @param title [#to_s]
712
+ # section title
713
+ # @param text [*#to_s]
714
+ # optional objects to print line by line
715
+ # @param options [{Symbol => Object}]
716
+ # print options – see {puts}
717
+ # @return [Section]
718
+ # itself
719
+
720
+ # Create a visually separated section with title for the output of text
686
721
  # elements.
687
- #
688
- # @macro like_msg
689
- def message(title, *text, &block) = __sec(:message, title, text, &block)
722
+ # @macro msg_like
723
+ def message(title, *text, **options, &block)
724
+ __sec(:message, false, title, text, options, &block)
725
+ end
690
726
  alias msg message
691
727
 
692
- # Create a visually separated section marked as informational with a title
693
- # for the output of text elements.
728
+ # Create a visually separated section marked as informational with title for
729
+ # the output of text elements.
694
730
  #
695
- # @macro like_msg
696
- def information(title, *text, &block)
697
- __tsec(:information, title, text, &block)
731
+ # @macro msg_like
732
+ def information(title, *text, **options, &block)
733
+ __sec(:information, true, title, text, options, &block)
698
734
  end
699
735
  alias info information
700
736
 
701
- # Create a visually separated section marked as a warning with a title for
737
+ # Create a visually separated section marked as warning with title for
702
738
  # the output of text elements.
703
739
  #
704
- # @macro like_msg
705
- def warning(title, *text, &block) = __tsec(:warning, title, text, &block)
740
+ # @macro msg_like
741
+ def warning(title, *text, **options, &block)
742
+ __sec(:warning, true, title, text, options, &block)
743
+ end
706
744
  alias warn warning
707
745
 
708
- # Create a visually separated section marked as an error with a title for
746
+ # Create a visually separated section marked as error with title for
709
747
  # the output of text elements.
710
748
  #
711
- # @macro like_msg
712
- def error(title, *text, &block) = __tsec(:error, title, text, &block)
749
+ # @macro msg_like
750
+ def error(title, *text, **options, &block)
751
+ __sec(:error, true, title, text, options, &block)
752
+ end
713
753
  alias err error
714
754
 
715
- # Create a visually separated section marked as a failure with a title for
755
+ # Create a visually separated section marked as failure with title for
716
756
  # the output of text elements.
717
757
  #
718
- # @macro like_msg
719
- def failed(title, *text, &block) = __tsec(:failed, title, text, &block)
758
+ # @macro msg_like
759
+ def failed(title, *text, **options, &block)
760
+ __sec(:failed, true, title, text, options, &block)
761
+ end
720
762
 
721
763
  # Create a framed section.
764
+ # Like any other {Element} sections support all {Features}.
722
765
  #
723
- # @param text (see section)
724
- # @param align [:left, :right, :centered]
725
- # text alignment,
726
- # see {Attributes::Align}
727
- # @param border [Symbol]
728
- # kind of border,
729
- # see {Attributes::Border}
730
- # @param border_style [Enumerable<Symbol>]
731
- # style of border,
732
- # see {Attributes::BorderStyle}
733
- #
734
- # @yieldparam frame [Framed] itself
735
- #
736
- # @return (see section)
737
- def framed(*text, align: :left, border: :default, border_style: nil, &block)
766
+ # @overload framed(align: :left, border: :default, border_style: nil)
767
+ # @param align [:left, :right, :centered]
768
+ # text alignment,
769
+ # see {Attributes::Align}
770
+ # @param border [Symbol]
771
+ # kind of border,
772
+ # see {Attributes::Border}
773
+ # @param border_style [Enumerable<Symbol>]
774
+ # style of border,
775
+ # see {Attributes::BorderStyle}
776
+ # @yieldparam framed [Element]
777
+ # itself
778
+ # @return [Object] the result of the given block
779
+ # @overload framed(*text, align: :left, border: :default, border_style: nil, **options)
780
+ # @param text [*#to_s]
781
+ # optional objects to print line by line
782
+ # @param align [:left, :right, :centered]
783
+ # text alignment,
784
+ # see {Attributes::Align}
785
+ # @param border [Symbol]
786
+ # kind of border,
787
+ # see {Attributes::Border}
788
+ # @param border_style [Enumerable<Symbol>]
789
+ # style of border,
790
+ # see {Attributes::BorderStyle}
791
+ # @param options [{Symbol => Object}]
792
+ # print options – see {puts}
793
+ # @return [Element] itself
794
+ def framed(
795
+ *text,
796
+ align: :left,
797
+ border: :default,
798
+ border_style: nil,
799
+ **options,
800
+ &block
801
+ )
738
802
  __with(
739
803
  Framed.new(
740
804
  self,
741
805
  Utils.align(align),
742
806
  Theme.current.border(border),
743
- Utils.style(border_style),
744
- text
807
+ Utils.style(border_style)
745
808
  ),
809
+ *text,
810
+ **options,
746
811
  &block
747
812
  )
748
813
  end
@@ -751,14 +816,17 @@ module NattyUI
751
816
  #
752
817
  # @param title [#to_s]
753
818
  # task title text
754
- # @param text (see section)
819
+ # @param text [*#to_s]
820
+ # optional objects to print line by line
755
821
  # @param pin [true, false] whether to keep text "pinned"
822
+ # @param options [{Symbol => Object}]
823
+ # print options – see {puts}
756
824
  #
757
825
  # @yieldparam task [Task] itself
758
826
  #
759
- # @return (see section)
760
- def task(title, *text, pin: false, &block)
761
- __with(Task.new(self, title, text, pin), &block)
827
+ # @return [Task] itself
828
+ def task(title, *text, pin: false, **options, &block)
829
+ __with(Task.new(self, title, pin), *text, **options, &block)
762
830
  end
763
831
 
764
832
  #
@@ -971,9 +1039,18 @@ module NattyUI
971
1039
  # Display some temporary content.
972
1040
  # The content displayed in the block will be erased after the block ends.
973
1041
  #
974
- # @example Show tempoary information
1042
+ # @example
1043
+ # ui.temporary(<<~MSG) { ui.await }
1044
+ # This is a [i]temporary[/i] displayed text.
1045
+ # It will disappear when you press [b]ENTER[/b].
1046
+ # MSG
1047
+ #
1048
+ # @example
975
1049
  # ui.temporary do
976
- # ui.info 'Information', 'This text will disappear when you pressed ENTER.'
1050
+ # ui.information 'Hint' do
1051
+ # ui.puts 'This is a [i]temporary[/i] displayed text.'
1052
+ # ui.puts 'It will disappear when you press [b]ENTER[/b].'
1053
+ # end
977
1054
  # ui.await
978
1055
  # end
979
1056
  #
@@ -981,7 +1058,9 @@ module NattyUI
981
1058
  # itself
982
1059
  #
983
1060
  # @return (see section)
984
- def temporary(&block) = __with(Temporary.new(self), &block)
1061
+ def temporary(*text, **options, &block)
1062
+ __with(Temporary.new(self), *text, **options, &block)
1063
+ end
985
1064
 
986
1065
  #
987
1066
  # @!endgroup
@@ -989,29 +1068,32 @@ module NattyUI
989
1068
 
990
1069
  private
991
1070
 
992
- def __with(element, &block) = NattyUI.__send__(:with, element, &block)
1071
+ def __with(...) = NattyUI.__send__(:_with, ...)
993
1072
 
994
- def __sec(color, title, text, &block)
995
- __with(Section.new(self, title, text, color), &block)
996
- end
997
-
998
- def __tsec(color, title, text, &block)
999
- __sec(color, "#{Theme.current.mark(color)}#{title}", text, &block)
1073
+ def __sec(color, mark, title, text, options, &block)
1074
+ if title && !title.empty?
1075
+ title, *rest =
1076
+ Text.each_line(title, limit: columns - 9, ansi: Terminal.ansi?).to_a
1077
+ text.unshift(rest.join("\n")) unless rest.empty?
1078
+ end
1079
+ __with(Section.new(self, color, mark, title), *text, **options, &block)
1000
1080
  end
1001
1081
 
1002
1082
  def __await(yes, no)
1003
- while (event = Terminal.read_key_event&.name)
1083
+ Terminal.on_key_event do |event|
1084
+ event = event.name
1004
1085
  if (no == event) || (no.is_a?(Enumerable) && no.include?(event))
1005
1086
  return false
1006
1087
  end
1007
1088
  if (yes == event) || (yes.is_a?(Enumerable) && yes.include?(event))
1008
1089
  return true
1009
1090
  end
1091
+ true
1010
1092
  end
1011
1093
  end
1012
1094
  end
1013
1095
 
1014
- dir = __dir__ # call the function once
1096
+ dir = __dir__
1015
1097
 
1016
1098
  autoload :Framed, "#{dir}/framed.rb"
1017
1099
  autoload :Section, "#{dir}/section.rb"
@@ -29,7 +29,7 @@ module NattyUI
29
29
 
30
30
  private
31
31
 
32
- def initialize(parent, align, chars, style, msg)
32
+ def initialize(parent, align, chars, style)
33
33
  super(parent)
34
34
  @align = align
35
35
  if style
@@ -45,7 +45,6 @@ module NattyUI
45
45
  line = chars[10] * (parent.columns - 2)
46
46
  parent.puts("#{style}#{chars[0]}#{line}#{chars[2]}")
47
47
  @bottom = "#{style}#{chars[6]}#{line}#{chars[8]}"
48
- puts(*msg) unless msg.empty?
49
48
  end
50
49
  end
51
50
  end
@@ -9,7 +9,7 @@ module NattyUI
9
9
  pin_line = NattyUI.lines_written
10
10
  draw
11
11
  last = @current
12
- while (event = Terminal.read_key_event)
12
+ Terminal.on_key_event do |event|
13
13
  case event.name
14
14
  when 'Esc', 'Ctrl+c'
15
15
  return if @abortable
@@ -34,7 +34,7 @@ module NattyUI
34
34
  keys = @opts.keys
35
35
  @current = keys[keys.index(@current) + 1] || keys[0]
36
36
  end
37
- next if last == @current
37
+ next true if last == @current
38
38
  pin_line = NattyUI.back_to_line(pin_line, erase: false)
39
39
  draw
40
40
  last = @current
@@ -55,6 +55,9 @@ module NattyUI
55
55
  self
56
56
  end
57
57
 
58
+ # @private
59
+ def end = NattyUI.__send__(:_end, self)
60
+
58
61
  alias _to_s to_s
59
62
  private :_to_s
60
63
 
@@ -82,7 +85,6 @@ module NattyUI
82
85
 
83
86
  def _done(text)
84
87
  NattyUI.back_to_line(@pin_line)
85
- @pin_line = nil
86
88
  cm = Theme.current.mark(:checkmark)
87
89
  @parent.puts(
88
90
  *text,
@@ -94,7 +96,6 @@ module NattyUI
94
96
 
95
97
  def _failed
96
98
  NattyUI.back_to_line(NattyUI.lines_written - 1) if @last&.size == 2
97
- @pin_line = nil
98
99
  end
99
100
 
100
101
  def initialize(parent, title, max, pin)
@@ -106,8 +107,8 @@ module NattyUI
106
107
  @style = Theme.current.task_style
107
108
  cm = Theme.current.mark(:current)
108
109
  @redraw_opts = {
109
- first_line_prefix: "#{cm} #{@style}",
110
- first_line_prefix_width: cm.width + 1
110
+ first_line_prefix: "#{cm}#{@style}",
111
+ first_line_prefix_width: cm.width
111
112
  }
112
113
  max ? self.max = max : redraw
113
114
  end
@@ -130,7 +131,6 @@ module NattyUI
130
131
  size = [@parent.columns, 72].min - 11
131
132
  percent = format('%5.2f', 100.0 * diff)
132
133
  return percent if size < 10
133
- return if percent == '100.00'
134
134
  fill = '█' * (size * diff)
135
135
  "#{percent}% [bright_black]┃#{@style}#{fill}[bright_black]#{
136
136
  '░' * (size - fill.size)
@@ -3,7 +3,7 @@
3
3
  require_relative 'element'
4
4
 
5
5
  module NattyUI
6
- # Display section used by
6
+ # Display section instance used by
7
7
  #
8
8
  # - {Features.section}
9
9
  # - {Features.message}
@@ -11,6 +11,7 @@ module NattyUI
11
11
  # - {Features.warning}
12
12
  # - {Features.error}
13
13
  # - {Features.failed}
14
+ # - {Features.framed}
14
15
  #
15
16
  class Section < Element
16
17
  include WithStatus
@@ -42,25 +43,13 @@ module NattyUI
42
43
  )
43
44
  end
44
45
 
45
- def initialize(parent, title, msg, kind)
46
+ def initialize(parent, kind, mark, title)
46
47
  super(parent)
47
- title, rest = split(title) if title && !title.empty?
48
48
  @border = Theme.current.section_border(kind)
49
- show_title(title)
49
+ mark &&= Theme.current.mark(kind)
50
+ show_title(mark ? "#{mark}#{title}" : title)
50
51
  @prefix = @border.prefix
51
52
  @prefix_width = @prefix.width
52
- puts(*rest) if rest && !rest.empty?
53
- puts(*msg) unless msg.empty?
54
- end
55
-
56
- def split(title)
57
- rest =
58
- Text.each_line(
59
- title,
60
- limit: @parent.columns - 9,
61
- ansi: Terminal.ansi?
62
- ).to_a
63
- [rest.shift, rest]
64
53
  end
65
54
  end
66
55
  end
@@ -5,7 +5,7 @@ require_relative 'theme'
5
5
  module NattyUI
6
6
  module ShellRenderer
7
7
  def self.sh(parent, cmd, options, space)
8
- opts_out, opts_err = opts_fom(Theme.current)
8
+ opts_out, opts_err = opts_from(Theme.current)
9
9
  Terminal.sh(*cmd, **options) do |line, kind|
10
10
  parent.puts(
11
11
  space ? line.gsub("\t", '    ').gsub(/[[:space:]]/, ' ') : line,
@@ -15,7 +15,7 @@ module NattyUI
15
15
  end
16
16
 
17
17
  if Terminal.ansi?
18
- def self.opts_fom(theme)
18
+ def self.opts_from(theme)
19
19
  out = theme.mark(:sh_out)
20
20
  err = theme.mark(:sh_err)
21
21
  [
@@ -56,7 +56,7 @@ module NattyUI
56
56
  ]
57
57
  end
58
58
  else
59
- def self.opts_fom(theme)
59
+ def self.opts_from(theme)
60
60
  out = theme.mark(:sh_out)
61
61
  err = theme.mark(:sh_err)
62
62
  [
data/lib/natty-ui/task.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require_relative 'element'
4
4
 
5
5
  module NattyUI
6
- # Task display section used by {Features.task}.
6
+ # Task section used by {Features.task}.
7
7
  #
8
8
  class Task < Temporary
9
9
  include WithStatus
@@ -27,7 +27,7 @@ module NattyUI
27
27
  @start_line = nil
28
28
  end
29
29
 
30
- def initialize(parent, title, msg, pin)
30
+ def initialize(parent, title, pin)
31
31
  super(parent)
32
32
  @title = title
33
33
  @pin = pin
@@ -42,7 +42,6 @@ module NattyUI
42
42
  prefix: "#{@prefix}#{style}",
43
43
  prefix_width: cm.width
44
44
  )
45
- puts(*msg) unless msg.empty?
46
45
  end
47
46
  end
48
47
  end
@@ -163,6 +163,8 @@ module NattyUI
163
163
  current: '➔',
164
164
  choice: '◦',
165
165
  current_choice: '◉',
166
+ option: '[ ]',
167
+ option_selected: '[X]',
166
168
  sh_out: ':',
167
169
  sh_err: '𝙓'
168
170
  }
@@ -243,8 +245,8 @@ module NattyUI
243
245
  # [current?][selected?]
244
246
  c = @mark[:current_choice]
245
247
  n = @mark[:none]
246
- sel = @mark[:checkmark]
247
- uns = @mark[:choice]
248
+ uns = @mark[:option]
249
+ sel = @mark[:option_selected]
248
250
  {
249
251
  false => { false => n + uns, true => n + sel }.compare_by_identity,
250
252
  true => { false => c + uns, true => c + sel }.compare_by_identity
@@ -348,6 +350,8 @@ module NattyUI
348
350
  current: '[bright_green]➔[/fg]',
349
351
  choice: '[bright_white]◦[/fg]',
350
352
  current_choice: '[bright_green]➔[/fg]',
353
+ option: '[dim][ ][/dim]',
354
+ option_selected: '[dim][[/dim][green]X[/fg][dim]][/dim]',
351
355
  sh_out: '[bright_white]:[/fg]',
352
356
  sh_err: '[red]𝙓[/fg]'
353
357
  )
@@ -379,6 +383,8 @@ module NattyUI
379
383
  current: '➡️',
380
384
  choice: '[bright_white]•[/fg]',
381
385
  current_choice: '[bright_green]●[/fg]',
386
+ option: '[dim][ ][/dim] ',
387
+ option_selected: '[dim][[/dim][green]X[/fg][dim]][/dim]',
382
388
  sh_out: '[white b]:[/]',
383
389
  sh_err: '❗️'
384
390
  )
@@ -2,5 +2,5 @@
2
2
 
3
3
  module NattyUI
4
4
  # The version number of the gem.
5
- VERSION = '0.33.0'
5
+ VERSION = '0.35.0'
6
6
  end
data/lib/natty-ui.rb CHANGED
@@ -50,7 +50,7 @@ module NattyUI
50
50
  # configured title
51
51
  # @return [nil]
52
52
  # when no title was set
53
- def title = @title_stack.last
53
+ def title = @title_stack[-1]
54
54
 
55
55
  # @attribute [w] title
56
56
  def title=(value)
@@ -85,32 +85,39 @@ module NattyUI
85
85
  @lines_written = number
86
86
  end
87
87
 
88
- # @private
89
- def alternate_screen
90
- return yield unless Terminal.ansi?
91
- begin
92
- Terminal.raw_write(Ansi::SCREEN_ALTERNATE)
93
- yield
94
- ensure
95
- Terminal.raw_write(Ansi::SCREEN_ALTERNATE_OFF)
96
- end
97
- end
98
-
99
88
  private
100
89
 
101
- def with(element)
90
+ def _begin(element)
102
91
  Terminal.hide_cursor if @element == self
103
- current, @element = @element, element
104
- yield(element) if block_given?
105
- ensure
106
- element.done if element.respond_to?(:done)
107
- Terminal.show_cursor if (@element = current) == self
92
+ @stack << element
93
+ @element = element
94
+ end
95
+
96
+ def _end(element)
97
+ idx = @stack.index(element) or return @element
98
+ @stack.drop(idx).each { _1.done if _1.respond_to?(:done) }
99
+ @stack = @stack.take(idx)
100
+ Terminal.show_cursor if (@element = @stack[-1]) == self
101
+ @element
102
+ end
103
+
104
+ def _with(element, *text, **options, &block)
105
+ _begin(element)
106
+ element.puts(*text, **options) unless text.empty?
107
+ return element unless block
108
+ begin
109
+ yield(element)
110
+ ensure
111
+ _end(element)
112
+ end
108
113
  end
109
114
  end
110
115
 
111
- @element = self
116
+ @stack = [@element = self]
112
117
  @lines_written = 0
113
118
  @title_stack = []
119
+
120
+ autoload :VERSION, "#{__dir__}/natty-ui/version.rb"
114
121
  end
115
122
 
116
123
  unless defined?(Kernel.ui)
data/natty-ui.gemspec ADDED
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/natty-ui/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'natty-ui'
7
+ spec.version = NattyUI::VERSION
8
+ spec.summary = <<~SUMMARY.tr("\n", ' ')
9
+ This is the beautiful, nice, nifty, fancy, neat, pretty, cool, rich,
10
+ lovely, natty user interface you like to have for your CLI.
11
+ SUMMARY
12
+ spec.description = <<~DESCRIPTION.tr("\n", ' ')
13
+ This is the beautiful, nice, nifty, fancy, neat, pretty, cool, rich, lovely,
14
+ natty user interface tool you like to have for your command line
15
+ applications. It contains elegant, simple and beautiful features that
16
+ enhance your command line interfaces functionally and aesthetically.
17
+ DESCRIPTION
18
+
19
+ spec.author = 'Mike Blumtritt'
20
+ spec.licenses = %w[MIT Ruby]
21
+ spec.homepage = 'https://codeberg.org/mblumtritt/natty-ui'
22
+ spec.metadata['source_code_uri'] = spec.homepage
23
+ spec.metadata['bug_tracker_uri'] = "#{spec.homepage}/issues"
24
+ spec.metadata['documentation_uri'] = 'https://rubydoc.info/gems/natty-ui'
25
+ spec.metadata['rubygems_mfa_required'] = 'true'
26
+ spec.metadata['yard.run'] = 'yard'
27
+
28
+ spec.required_ruby_version = '>= 3.0'
29
+ spec.add_dependency 'terminal_rb', '>= 0.17.2'
30
+
31
+ spec.files = Dir['lib/**/*.rb'] + Dir['examples/*.rb']
32
+ spec.files << 'natty-ui.gemspec' << '.yardopts'
33
+ spec.extra_rdoc_files = %w[README.md]
34
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: natty-ui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.33.0
4
+ version: 0.35.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Blumtritt
@@ -15,27 +15,24 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: 0.14.0
18
+ version: 0.17.2
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: 0.14.0
26
- description: |
27
- This is the beautiful, nice, nifty, fancy, neat, pretty, cool, rich, lovely,
28
- natty user interface tool you like to have for your command line applications.
29
- It contains elegant, simple and beautiful features that enhance your
30
- command line interfaces functionally and aesthetically.
25
+ version: 0.17.2
26
+ description: 'This is the beautiful, nice, nifty, fancy, neat, pretty, cool, rich,
27
+ lovely, natty user interface tool you like to have for your command line applications.
28
+ It contains elegant, simple and beautiful features that enhance your command line
29
+ interfaces functionally and aesthetically. '
31
30
  executables: []
32
31
  extensions: []
33
32
  extra_rdoc_files:
34
- - LICENSE
35
33
  - README.md
36
34
  files:
37
35
  - ".yardopts"
38
- - LICENSE
39
36
  - README.md
40
37
  - examples/24bit-colors.rb
41
38
  - examples/3bit-colors.rb
@@ -81,9 +78,11 @@ files:
81
78
  - lib/natty-ui/version.rb
82
79
  - lib/natty-ui/width_finder.rb
83
80
  - lib/natty_ui.rb
81
+ - natty-ui.gemspec
84
82
  homepage: https://codeberg.org/mblumtritt/natty-ui
85
83
  licenses:
86
- - BSD-3-Clause
84
+ - MIT
85
+ - Ruby
87
86
  metadata:
88
87
  source_code_uri: https://codeberg.org/mblumtritt/natty-ui
89
88
  bug_tracker_uri: https://codeberg.org/mblumtritt/natty-ui/issues
@@ -104,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
104
103
  - !ruby/object:Gem::Version
105
104
  version: '0'
106
105
  requirements: []
107
- rubygems_version: 3.6.9
106
+ rubygems_version: 4.0.1
108
107
  specification_version: 4
109
108
  summary: This is the beautiful, nice, nifty, fancy, neat, pretty, cool, rich, lovely,
110
109
  natty user interface you like to have for your CLI.
data/LICENSE DELETED
@@ -1,28 +0,0 @@
1
- BSD 3-Clause License
2
-
3
- Copyright (c) 2017-2025, Mike Blumtritt. All rights reserved.
4
-
5
- Redistribution and use in source and binary forms, with or without
6
- modification, are permitted provided that the following conditions are met:
7
-
8
- 1. Redistributions of source code must retain the above copyright notice, this
9
- list of conditions and the following disclaimer.
10
-
11
- 2. Redistributions in binary form must reproduce the above copyright notice,
12
- this list of conditions and the following disclaimer in the documentation
13
- and/or other materials provided with the distribution.
14
-
15
- 3. Neither the name of the copyright holder nor the names of its
16
- contributors may be used to endorse or promote products derived from
17
- this software without specific prior written permission.
18
-
19
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.