terminal_rb 1.0.6 → 1.0.9

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: 8f2f93f672f13936c9415cf3a474533df0f5785da3272f3482a02842a0607917
4
- data.tar.gz: 52a1498c80085ea7667b7641416f406ae8694e02505a0c3201efd5f150f8d986
3
+ metadata.gz: 8a5e3a8b375cd45eef93b2316251d14da8732b80e27fb1192bbf8fb87a9bf297
4
+ data.tar.gz: f10bcbf23b3e8ecd01bc51a8772f2099dbe5950bb561bc26d854e6c3d2fb382d
5
5
  SHA512:
6
- metadata.gz: 41cfbe3e34fa3db0af51674dc0f70f32dd08fb20d2b3a1040eff2834c65440f917f2ecea021500037658fbcf613373148c8399fdb29b7ea987916e71c570a65d
7
- data.tar.gz: b6485ac5e2a9ba17ec72ffadc94cad6f5348ab846f885d12e9a68a338c15baeaa0d564ce1e1a4d2f946505887782e02dfd7221fc03e60bd3e7bb40afb88b47a1
6
+ metadata.gz: a3c0cbfe105fccd17923c6ba7f43faf797b2df1fcc5d76d8bf138b1593156c5e23dc2a9700be17fa61e8f744151bb756943a482f9eca501087ffabb257e7e608
7
+ data.tar.gz: 60616eb76bbc7d01e1759274220390611887bbafe12f2d3b7cda8eb68dcd6cee491ce6b477235ce9a8b5e43b372e6d3e6c7a8ff81962a67eb78a20cb58fe7523
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
- # Terminal.rb v1.0.6
1
+ # Terminal.rb v1.0.9
2
2
 
3
3
  Terminal.rb supports you with input and output on your terminal. Simple [BBCode](https://en.wikipedia.org/wiki/BBCode)-like markup for attributes and coloring, word-wise line breaks, correct special key recognition and mouse event reporting enable you to implement your CLI app quickly and easily.
4
4
 
5
5
  - Gem: [rubygems.org](https://rubygems.org/gems/terminal_rb)
6
6
  - Source: [codeberg.org](https://codeberg.org/mblumtritt/Terminal.rb)
7
- - Help: [rubydoc.info](https://rubydoc.info/gems/terminal_rb/1.0.6/Terminal)
7
+ - Help: [rubydoc.info](https://rubydoc.info/gems/terminal_rb/1.0.9/Terminal)
8
8
 
9
9
  ## Features
10
10
 
data/examples/bbcode.rb CHANGED
@@ -6,28 +6,28 @@ Terminal.puts <<~TEXT
6
6
 
7
7
  ✅ [b bright_green]Terminal.rb::Ansi[/b] — BBCode:[/]
8
8
 
9
- [b]Bold[/b] [\\b][d]...[/d][\\/b] [\\bold][d] ... [/d][\\/bold]
10
- [dim]Dim[/dim] [\\d][d]...[/d][\\/d] [\\dim][d] ... [/d][\\/dim]
11
- [i]Italic[/i] [\\i][d]...[/d][\\/i] [\\italic][d] ... [/d][\\/italic]
9
+ [b]Bold[/b] [\\b][d]...[/d][\\/b] [\\bold][d]........[/d][\\/bold]
10
+ [dim]Dim[/dim] [\\d][d]...[/d][\\/d] [\\dim][d].........[/d][\\/dim]
11
+ [i]Italic[/i] [\\i][d]...[/d][\\/i] [\\italic][d]......[/d][\\/italic]
12
12
  [u]Underline[/u] [\\u][d]...[/d][\\/u] [\\underline][d]...[/d][\\/underline]
13
- [h]Hide[/h] [\\h][d]...[/d][\\/h] [\\hide][d] ... [/d][\\/hide]
13
+ [h]Hide[/h] [\\h][d]...[/d][\\/h] [\\hide][d]........[/d][\\/hide]
14
14
 
15
- [inv]Invert[/inv] [\\inv][d] ... [/d][\\/inv] [\\invert][d]...[/d][\\/invert]
15
+ [inv]Invert[/inv] [\\inv][d]......[/d][\\/inv] [\\invert][d]...[/d][\\/invert]
16
16
  [strike]Strike[/strike] [\\strike][d]...[/d][\\/strike]
17
- [blink]Blink[/blink] [\\blink][d] ...[/d][\\/blink]
17
+ [blink]Blink[/blink] [\\blink][d]....[/d][\\/blink]
18
18
 
19
- [u]Underline[/u] [\\u][d] ... [/d][\\/u] [\\underline][d] ... [/d][\\/underline]
20
- [uu]Double underline[/uu] [\\uu][d] ...[/d][\\/uu] [\\double_underline][d]...[/d][\\/double_underline]
21
- [cu]Curly underline[/cu] [\\cu][d] ...[/d][\\/cu] [\\curly_underline][d] ...[/d][\\/curly_underline]
19
+ [u]Underline[/u] [\\u][d].....[/d][\\/u] [\\underline][d]..........[/d][\\/underline]
20
+ [uu]Double underline[/uu] [\\uu][d]....[/d][\\/uu] [\\double_underline][d]...[/d][\\/double_underline]
21
+ [cu]Curly underline[/cu] [\\cu][d]....[/d][\\/cu] [\\curly_underline][d]...[/d] [\\/curly_underline]
22
22
  [dau]Dashed underline[/dau] [\\dau][d]...[/d][\\/dau] [\\dashed_underline][d]...[/d][\\/dashed_underline]
23
23
  [dou]Dotted underline[/dou] [\\dou][d]...[/d][\\/dou] [\\dotted_underline][d]...[/d][\\/dotted_underline]
24
24
 
25
- [fraktur]Fraktur[/fraktur] [\\fraktur][d] ... [/d][\\/fraktur]
26
- [framed]Framed[/framed] [\\framed][d] ... [/d][\\/framed]
25
+ [fraktur]Fraktur[/fraktur] [\\fraktur][d].....[/d][\\/fraktur]
26
+ [framed]Framed[/framed] [\\framed][d]......[/d][\\/framed]
27
27
  [encircled]Encircled[/encircled] [\\encircled][d]...[/d][\\/encircled]
28
28
 
29
- [ovr]Overlined[/ovr] [\\ovr][d]...[/d][\\/ovr] [\\overlined][d] ... [/d][\\/overlined]
30
- [sub]Subscript[/sub] [\\sub][d]...[/d][\\/sub] [\\subscript][d] ... [/d][\\/subscript]
29
+ [ovr]Overlined[/ovr] [\\ovr][d]...[/d][\\/ovr] [\\overlined][d].....[/d][\\/overlined]
30
+ [sub]Subscript[/sub] [\\sub][d]...[/d][\\/sub] [\\subscript][d].....[/d][\\/subscript]
31
31
  [sup]Superscript[/sup] [\\sup][d]...[/d][\\/sup] [\\superscript][d]...[/d][\\/superscript]
32
32
 
33
33
  TEXT
@@ -6,7 +6,7 @@ Terminal.puts <<~TEXT
6
6
 
7
7
  ✅ [b bright_green]Terminal.rb[/b] — Key Codes:[/fg]
8
8
  Press any key to display it's control code and name.
9
- [bright_black]([b]#{Terminal.input_mode}[/b] mode - exit with ESC)[/fg]
9
+ [bright_black]([b]#{Terminal.input_mode}[/b] mode – exit with ESC)[/fg]
10
10
 
11
11
  TEXT
12
12
 
data/lib/terminal/ansi.rb CHANGED
@@ -16,7 +16,7 @@ module Terminal
16
16
  # Terminal::Ansi[:on_blue] # => "\e[44m"
17
17
  #
18
18
  # @example BBCode to ANSI
19
- # Terminal::Ansi.bbcode('[bold]Hello[/bold]') # => "\e[1mHello\e[m"
19
+ # Terminal::Ansi.bbcode('[bold]Hello[/bold]') # => "\e[1mHello\e[22m"
20
20
  #
21
21
  module Ansi
22
22
  class << self
@@ -39,7 +39,7 @@ module Terminal
39
39
  def named_colors = NAMED_COLORS.keys.map!(&:to_sym)
40
40
 
41
41
  #
42
- # @!group ANSI Control Code Generation
42
+ # @!group Text Attribute Code Generation
43
43
  #
44
44
 
45
45
  # Generate an ANSI escape sequence from attribute names or color
@@ -67,20 +67,12 @@ module Terminal
67
67
  "\e[#{
68
68
  attributes
69
69
  .map! do |arg|
70
- case arg
71
- when String
72
- @attr_map[arg] || _invalid(arg)
73
- when Symbol
74
- @attrs_map[arg] || _invalid(arg)
75
- when (0..255)
76
- "38;5;#{arg}"
77
- when (256..511)
78
- "48;5;#{arg - 256}"
79
- when (512..767)
80
- "58;5;#{arg - 512}"
81
- else
82
- _invalid(arg)
83
- end
70
+ next @attr_str_map[arg] || _invalid(arg) if arg.is_a?(String)
71
+ next @attr_sym_map[arg] || _invalid(arg) if arg.is_a?(Symbol)
72
+ _invalid(arg) unless arg.is_a?(Integer)
73
+ next "38;5;#{arg}" if arg >= 0 && arg <= 255
74
+ next "48;5;#{arg - 256}" if arg >= 256 && arg <= 511
75
+ arg >= 512 && arg <= 767 ? "58;5;#{arg - 512}" : _invalid(arg)
84
76
  end
85
77
  .join(';')
86
78
  }m"
@@ -130,9 +122,11 @@ module Terminal
130
122
  # attribute is invalid
131
123
  def try_convert(attributes, separator: ' ')
132
124
  return unless attributes
133
- return if (attributes = attributes.to_s.split(separator)).empty?
134
- attributes.uniq!
135
- "\e[#{attributes.map! { @attr_map[it] || return }.join(';')}m"
125
+ unless (attributes = attributes.to_s.split(separator)).empty?
126
+ # attributes.uniq!
127
+ codes = attributes.map { @attr_str_map[it] || break }
128
+ "\e[#{codes.join(';')}m" if codes
129
+ end
136
130
  end
137
131
 
138
132
  # Generate rainbow-colored text using true color (24-bit) ANSI
@@ -163,7 +157,7 @@ module Terminal
163
157
  #
164
158
  # @!endgroup
165
159
  #
166
- # @!group BBcode Generation
160
+ # @!group BBCode Generation
167
161
  #
168
162
 
169
163
  # Convert BBCode markup to ANSI escape codes.
@@ -186,11 +180,16 @@ module Terminal
186
180
  def bbcode(str)
187
181
  str = str.to_s
188
182
  return str.dup unless str.index('[')
189
- str.gsub(@re_bbcode) do |match_str|
190
- match = Regexp.last_match(1) or next match_str
191
- next try_convert(match) || match_str if match[0] != '\\'
192
- match[0] = ''
193
- "[#{match}]"
183
+ str.gsub(@re_bbcode) do |match|
184
+ hit = @bbcc[match] and next hit
185
+ next "[#{match[2..]}" if (fc = match[1]) == '\\'
186
+ @bbcc.clear if @bbcc.size == 1024
187
+ @bbcc[match] = if fc == ']' || (att = match[1..-2].split).empty?
188
+ match
189
+ else
190
+ att = att.map { @attr_str_map[it] || break }
191
+ att ? "\e[#{att.join(';')}m" : match
192
+ end
194
193
  end
195
194
  end
196
195
 
@@ -206,15 +205,12 @@ module Terminal
206
205
  def unbbcode(str)
207
206
  str = str.to_s
208
207
  return str.dup unless str.index('[')
209
- str.gsub(@re_bbcode) do |match_str|
210
- match = Regexp.last_match(1) or next match_str
211
- if match[0] == '\\'
212
- match[0] = ''
213
- next "[#{match}]"
214
- end
215
- next match_str if (match = match.split).empty?
216
- next if match.all? { @attr_map[it] }
217
- match_str
208
+ str.gsub(@re_bbcode) do |match|
209
+ hit = @bbcc[match] and next hit[0] == '[' ? hit : nil
210
+ next match if (fc = match[1]) == ']'
211
+ next "[#{match[2..]}" if fc == '\\'
212
+ next match if (att = match[1..-2].split).empty?
213
+ match unless att.map { @attr_str_map[it] || break }
218
214
  end
219
215
  end
220
216
 
@@ -232,13 +228,11 @@ module Terminal
232
228
  def escape_bbcode(str)
233
229
  str = str.to_s
234
230
  return str.dup unless str.index('[')
235
- str.gsub(@re_bbcode) do |match_str|
236
- fc = match_str[1]
237
- next match_str if fc == '\\' || fc == ']'
238
- match = Regexp.last_match(1) or next match_str
239
- next match_str if (parts = match.split).empty?
240
- next "[\\#{match}]" if parts.all? { @attr_map[it] }
241
- match_str
231
+ str.gsub(@re_bbcode) do |match|
232
+ hit = @bbcc[match] and hit[1] == '\\' ? hit : "[\\#{match[1..]}"
233
+ next match if (fc = match[1]) == '\\' || fc == ']'
234
+ next match if (att = match[1..-2].split).empty?
235
+ att.map { @attr_str_map[it] || break } ? "[\\#{match[1..]}" : match
242
236
  end
243
237
  end
244
238
 
@@ -269,14 +263,9 @@ module Terminal
269
263
  # @return [true, false]
270
264
  def valid?(*attributes)
271
265
  attributes.all? do |arg|
272
- case arg
273
- when String
274
- @attr_map[arg]
275
- when Symbol
276
- @attrs_map[arg]
277
- when (0..767)
278
- true
279
- end
266
+ next @attr_str_map[arg] if arg.is_a?(String)
267
+ next @attr_sym_map[arg] if arg.is_a?(Symbol)
268
+ arg.is_a?(Integer) && arg >= 0 && arg <= 767
280
269
  end
281
270
  end
282
271
 
@@ -298,7 +287,7 @@ module Terminal
298
287
  #
299
288
  # @!endgroup
300
289
  #
301
- # @!group ANSI Control Code Generation
290
+ # @!group Cursor and Screen Control Codes
302
291
  #
303
292
 
304
293
  # Move cursor up.
@@ -453,54 +442,26 @@ module Terminal
453
442
  # @return (see .[])
454
443
  def line_erase(part = :all) = "\e[#{@line_erase[part]}K"
455
444
 
456
- # Set the terminal window title.
457
- # @note Supported by Hyper, iTerm2, Kitty, macOS Terminal, Tabby,
458
- # WezTerm.
459
- #
460
- # @example
461
- # Terminal.raw_write(Terminal::Ansi.title('My App'))
462
- #
463
- # @param title [String] the title text
464
- # @return (see .[])
465
- def title(title) = "\e]0;#{title}\a"
466
-
467
- # Start a hyperlink (OSC 8).
468
- #
469
- # @note Supported by Ghostty, iTerm2, Kitty, Rio, Tabby, WezTerm.
470
- #
471
- # @see .link
472
- # @see .link_end
473
445
  #
474
- # @param url [#to_s] the link URL
475
- # @param params [Hash] optional link parameters (e.g., `id:`)
476
- # @return (see .[])
477
- def link_start(url, **params)
478
- "\e]8;#{params.map { it.join('=') }.join(':')};#{url}\a"
479
- end
480
-
481
- # End a hyperlink.
446
+ # @!endgroup
482
447
  #
483
- # @see .link_start
448
+ # @!group Terminal Integration Codes
484
449
  #
485
- # @return (see .[])
486
- def link_end = +LINK_END
487
450
 
488
- # Create a complete hyperlink (OSC 8).
451
+ # Set the terminal window title.
489
452
  #
490
- # @see .link_start
491
- # @see .link_end
453
+ # @note Supported by iTerm2, Kitty, macOS Terminal, Tabby,
454
+ # WezTerm.
492
455
  #
493
456
  # @example
494
- # Terminal::Ansi.link('https://example.com', 'Example')
457
+ # Terminal.raw_write(Terminal::Ansi.title('My App'))
495
458
  #
496
- # @param (see .link_start)
497
- # @param text [String] the visible link text
459
+ # @param title [#to_s] the title text
498
460
  # @return (see .[])
499
- def link(url, text, **params)
500
- "#{link_start(url, **params)}#{text}#{LINK_END}"
501
- end
461
+ def title(title) = "\e]0;#{_no_bell(title)}\a"
502
462
 
503
463
  # Send a desktop notification via the terminal.
464
+ #
504
465
  # @note Supported by Ghostty, iTerm2, Kitty, WezTerm.
505
466
  #
506
467
  # @example
@@ -508,7 +469,7 @@ module Terminal
508
469
  #
509
470
  # @param text [to_s] the notification text
510
471
  # @return (see .[])
511
- def notify(text) = "\e]9;#{text}\a"
472
+ def notify(text) = "\e]9;#{_no_bell(text)}\a"
512
473
 
513
474
  # Show or update a progress indicator in the terminal tab/title
514
475
  # bar.
@@ -545,10 +506,100 @@ module Terminal
545
506
  "\e]9;4;#{state};#{percent.to_i.clamp(0, 100)}\a"
546
507
  end
547
508
 
509
+ # @comment Should we implement this?
510
+ #
511
+ # # Request attention.
512
+ # #
513
+ # # @note Supported by iTerm2.
514
+ # #
515
+ # # @param action [true, :bounce, :once, :fireworks, false, :stop]
516
+ # # start bouncing dock icon (`true`, `:bounce`),
517
+ # # bounce dock icon once (`:once`)
518
+ # # or stop attention (`false`, `:stop`)
519
+ # # @return (see .[])
520
+ # def attention(action = :bounce)
521
+ # "\e]1337;RequestAttention=#{
522
+ # case action
523
+ # when true, :bounce
524
+ # 'yes'
525
+ # when :once, :fireworks
526
+ # action
527
+ # else
528
+ # 'no'
529
+ # end
530
+ # }\a"
531
+ # end
532
+
533
+ # Create or start a hyperlink.
534
+ #
535
+ # @note Supported by Ghostty, iTerm2, Kitty, Rio, Tabby, WezTerm.
536
+ #
537
+ # @see .link_end
538
+ #
539
+ # @example
540
+ # Terminal::Ansi.link('https://example.com', 'Example')
541
+ #
542
+ # @param url [#to_s] the link URL
543
+ # @param text [nil, #to_s] the visible link text
544
+ # @param params [Hash] optional link parameters (e.g., `id:`)
545
+ # @return (see .[])
546
+ def link(url, text = nil, **params)
547
+ start =
548
+ "\e]8;#{
549
+ _no_bell(params.map { it.join('=') }.join(':'))
550
+ };#{_no_bell(url)}\a"
551
+ text ? "#{start}#{_no_bell(text)}#{LINK_END}" : start
552
+ end
553
+
554
+ # End a hyperlink.
555
+ #
556
+ # @see .link
557
+ #
558
+ # @return (see .[])
559
+ def link_end = +LINK_END
560
+
561
+ # Copy text to clipboard.
562
+ #
563
+ # @note Supported by Ghostty, iTerm2, Kitty, Rio, Tabby, WezTerm.
564
+ #
565
+ # @param text [#to_s] text to copy
566
+ # @return (see .[])
567
+ def clipboard(text) = "\e]52;c;#{[text.to_s].pack('m0')}\a"
568
+
569
+ # Show or update the session status of the terminal.
570
+ #
571
+ # @note Supported by iTerm2.
572
+ #
573
+ # @example
574
+ # Terminal.raw_write(Terminal::Ansi.status(text: 'Working'))
575
+ # Terminal.raw_write(Terminal::Ansi.status(indicator: '#fa00fa'))
576
+ # Terminal.raw_write(Terminal::Ansi.status(indicator: :f00))
577
+ # Terminal.raw_write(Terminal::Ansi.status) # reset the status
578
+ #
579
+ # @param text [nil, #to_s] status text
580
+ # @param detail [nil, #to_s] status detail text
581
+ # @param color [nil, String, Symbol] text color
582
+ # @param indicator [nil, String, Symbol] status indicator color
583
+ # @return (see .[])
584
+ def status(text: nil, detail: nil, color: nil, indicator: nil)
585
+ str = text ? ";status=#{_no_bell(text)}" : ''
586
+ indicator = _try_rgb(indicator) and str << ";indicator=#{indicator}"
587
+ color = _try_rgb(color) and str << ";color=#{color}"
588
+ str << "detail=#{_no_bell(detail)}" if detail
589
+ str.empty? ? +STATUS_RESET : "\e]21337#{str}\a"
590
+ end
591
+
592
+ # Reset session status of the terminal.
593
+ #
594
+ # @see .status
595
+ #
596
+ # @return (see .[])
597
+ def status_reset = +STATUS_RESET
598
+
548
599
  # Scale text using the
549
600
  # [text sizing protocol](https://sw.kovidgoyal.net/kitty/text-sizing-protocol).
550
601
  #
551
- # @note Only supported by Kitty.
602
+ # @note Supported by Kitty.
552
603
  #
553
604
  # @example Double-height Greeting
554
605
  # Terminal::Ansi.scale('Hello Ruby!', scale: 2)
@@ -614,18 +665,25 @@ module Terminal
614
665
  caller(1)
615
666
  )
616
667
  end
668
+
669
+ def _try_rgb(str)
670
+ mt = /\A?#?([[:xdigit:]]{3,6})\z/.match(str.to_s)&.[](1) or return
671
+ "##{mt.size == 6 ? mt : mt[..2]}"
672
+ end
673
+
674
+ def _no_bell(str) = str.to_s.tr("\a", '')
617
675
  end
618
676
 
619
677
  @re_test =
620
678
  /
621
- (?:\e\[[\x30-\x3f]*[\x20-\x2f]*[a-zA-Z])
622
- |
623
- (?:\e\]\d+(?:;[^\a\e]+)*(?:\a|\e\\))
624
- /x
679
+ (?:\e\[[\x30-\x3f]*[\x20-\x2f]*[a-zA-Z])
680
+ | (?:\e\]\d+(?:;[^\a\e]+)*(?:\a|\e\\))
681
+ /x
625
682
 
626
- @re_bbcode = /(?:\[((?~[\[\]]))\])/
683
+ @re_bbcode_old = /(?:\[((?~[\[\]]))\])/
684
+ @re_bbcode = /(?:\[(?~[\[\]]))\]/
627
685
 
628
- clr_map = {
686
+ color_map = {
629
687
  # foreground
630
688
  'black' => '30',
631
689
  'red' => '31',
@@ -684,9 +742,9 @@ module Terminal
684
742
  'on_bright_white' => '107'
685
743
  }
686
744
 
687
- @colors = clr_map.keys.map!(&:to_sym).sort!.freeze
745
+ @colors = color_map.keys.map!(&:to_sym).sort!.freeze
688
746
 
689
- attr_map = {
747
+ @attr_base_map = {
690
748
  'reset' => '',
691
749
  'bold' => '1',
692
750
  'faint' => '2',
@@ -743,7 +801,7 @@ module Terminal
743
801
  'dashed_underline_off' => '4:0'
744
802
  }
745
803
 
746
- attr_alias = ->(t, s) { attr_map[t] = attr_map[s] }
804
+ attr_alias = ->(t, s) { @attr_base_map[t] = @attr_base_map[s] }
747
805
 
748
806
  # aliases:
749
807
  attr_alias['off', 'reset']
@@ -768,18 +826,18 @@ module Terminal
768
826
  attr_alias['sup', 'superscript']
769
827
  attr_alias['sub', 'subscript']
770
828
 
771
- @attributes = attr_map.keys.map!(&:to_sym).sort!.freeze
829
+ @attributes = @attr_base_map.keys.map!(&:to_sym).sort!.freeze
772
830
 
773
831
  # shortcuts disable:
774
- attr_map.keys.each do |n|
832
+ @attr_base_map.keys.each do |n|
775
833
  attr_alias["/#{n.delete_suffix('_off')}", n] if n.end_with?('_off')
776
834
  end
777
835
 
778
836
  # additional shortcuts disable:
779
837
  attr_alias['/', 'reset']
780
- attr_map['/bg'] = clr_map['on_default']
781
- attr_map['/fg'] = clr_map['default']
782
- attr_map['/ul'] = clr_map['ul_default']
838
+ @attr_base_map['/bg'] = color_map['on_default']
839
+ @attr_base_map['/fg'] = color_map['default']
840
+ @attr_base_map['/ul'] = color_map['ul_default']
783
841
 
784
842
  attr_alias['/b', 'bold_off']
785
843
  attr_alias['/d', 'dim_off']
@@ -800,29 +858,33 @@ module Terminal
800
858
  @cbase.default = '38'
801
859
  @cbase.freeze
802
860
 
803
- @attr_map =
804
- Hash.new do |_, str|
805
- b, v = /\A(on|ul)?_?#?([[:xdigit:]]{1,6})\z/.match(str)&.captures
861
+ @attr_base_map.merge!(color_map).freeze
862
+
863
+ @attr_str_map = @attr_base_map.dup
864
+ @attr_str_map.default_proc =
865
+ proc do |h, s|
866
+ if h.size > 1024 # cache limit
867
+ h.keep_if { |key, _| @attr_base_map.key?(key) }
868
+ end
869
+ b, v = /\A(on|ul)?_?#?([[:xdigit:]]{1,6})\z/.match(s)&.captures
806
870
  unless v
807
- b = /\A(on|ul)?_?([a-z]{3,}[0-9]{0,3})\z/.match(str) or next
808
- v = NAMED_COLORS[b[2]] or next
809
- next "#{@cbase[b[1]]};#{v}"
871
+ b = /\A(on|ul)?_?([a-z]{3,}[0-9]{0,3})\z/.match(s) or next h[s] = nil
872
+ next h[s] = (v = NAMED_COLORS[b[2]]) ? "#{@cbase[b[1]]};#{v}" : nil
810
873
  end
811
- case v.size
812
- when 1, 2
813
- "#{@cbase[b]};5;#{v.hex}"
814
- when 3
815
- "#{@cbase[b]};2;#{(v[0] * 2).hex};#{(v[1] * 2).hex};#{(v[2] * 2).hex}"
816
- when 6
874
+ h[s] = if v.size == 6
817
875
  "#{@cbase[b]};2;#{v[0, 2].hex};#{v[2, 2].hex};#{v[4, 2].hex}"
876
+ elsif v.size == 3
877
+ "#{@cbase[b]};2;#{(v[0] * 2).hex};#{(v[1] * 2).hex};#{(v[2] * 2).hex}"
878
+ elsif v.size < 3
879
+ "#{@cbase[b]};5;#{v.hex}"
818
880
  end
819
881
  end
820
- attr_map.merge!(clr_map).keys.sort!.each { @attr_map[it] = attr_map[it] }
821
- @attr_map.freeze
822
882
 
823
- @attrs_map = @attr_map.transform_keys(&:to_sym)
824
- @attrs_map.default_proc = @attr_map.default_proc
825
- @attrs_map.compare_by_identity.freeze
883
+ @attr_sym_map = @attr_base_map.transform_keys(&:to_sym)
884
+ @attr_sym_map.compare_by_identity
885
+ @attr_sym_map.default_proc = proc { |h, s| h[s] = @attr_str_map[s.to_s] }
886
+
887
+ @bbcc = {} # BBCode cache
826
888
 
827
889
  @screen_erase = { below: nil, above: '1', scrollback: '3' }
828
890
  @screen_erase.default = '2'
@@ -907,6 +969,16 @@ module Terminal
907
969
  # @private
908
970
  LINE_ERASE_PREV = -"#{cursor_prev_line(nil)}#{LINE_ERASE}"
909
971
 
972
+ # @comment Supported by Terminal, iTerm, WezTerm:
973
+ # @private
974
+ LINE_DOUBLE_HEIGHT_TOP = "\e#3"
975
+ # @private
976
+ LINE_DOUBLE_HEIGHT_BOTTOM = "\e#4"
977
+ # @private
978
+ LINE_SINGLE_WIDTH = "\e#5"
979
+ # @private
980
+ LINE_DOUBLE_WIDTH = "\e#6"
981
+
910
982
  # @private
911
983
  LINK_END = "\e]8;;\a"
912
984
 
@@ -915,6 +987,9 @@ module Terminal
915
987
  # @private
916
988
  PROGRESS_SHOW_INDETERMINATE = "\e]9;4;3;0\a"
917
989
 
990
+ # @private
991
+ STATUS_RESET = "\e]21337;status=;indicator=;status-color=\a"
992
+
918
993
  # @comment seems not widely supported:
919
994
  # doubled: def cursor_column(column = 1) = "\e[#{column}`"
920
995
  # doubled: def cursor_row(row = 1) = "\e[#{row}d"
@@ -932,13 +1007,6 @@ module Terminal
932
1007
  #
933
1008
  # def set_scroll_region(top = nil, bottom = nil) = "\e[#{top};#{bottom}r"
934
1009
 
935
- # @comment other:
936
- # "\eE" same as "\r\n"
937
- # "\eD" same as "\n" but preserves column
938
- # "\eM" reverse "\n"
939
- # "\e[6n" report Cursor Position as "ESC \[ row ; col R"
940
- # "\e[21t report window’s title as ESC ] l title ESC \"
941
-
942
1010
  # @comment TODO:
943
1011
  # https://sw.kovidgoyal.net/kitty/desktop-notifications
944
1012
  # https://sw.kovidgoyal.net/kitty/pointer-shapes
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Terminal
4
4
  # @private
5
- module DumpInput
5
+ module DumbInput
6
6
  def input_mode = :dumb
7
7
 
8
8
  def on_key_event(*)
@@ -32,5 +32,5 @@ module Terminal
32
32
  end
33
33
  end
34
34
 
35
- private_constant :DumpInput
35
+ private_constant :DumbInput
36
36
  end
@@ -9,9 +9,9 @@ module Terminal
9
9
  # @see Terminal.read_key_event
10
10
  #
11
11
  # @example Parse a raw escape sequence
12
- # event = Terminal::KeyEvent["\r"]
13
- # event.key # => :Enter
14
- # event.name # => "Enter"
12
+ # event = Terminal::KeyEvent["a"]
13
+ # event.key # => "a"
14
+ # event.name # => "a"
15
15
  # event.simple? # => true
16
16
  #
17
17
  # @example Inspect modifier keys
@@ -33,7 +33,7 @@ module Terminal
33
33
  # @see #modifiers
34
34
  # @see #modifier?
35
35
  #
36
- # @return [Integer] modifier_bits bitmask
36
+ # @return [Integer] bitmask of the active modifiers
37
37
  attr_reader :modifier_bits
38
38
 
39
39
  # Names of active modifier keys.
@@ -14,7 +14,6 @@ module Terminal
14
14
  # @example
15
15
  # Terminal.input_mode # => :csi_u
16
16
  #
17
- # @attribute [r] input_mode
18
17
  # @return [Symbol] `:csi_u`, `:legacy`, `:dumb`, or `:error`
19
18
 
20
19
  #
@@ -33,7 +32,7 @@ module Terminal
33
32
  #
34
33
  # @example
35
34
  # Terminal.on_key_event(mouse: true) do |event|
36
- # break if event.name == 'ESC'
35
+ # break if event.name == 'Esc'
37
36
  # puts event.name
38
37
  # end
39
38
  #
@@ -69,7 +68,7 @@ module Terminal
69
68
  extend AnsiInput
70
69
  when :dumb
71
70
  require_relative('input/dumb')
72
- extend DumpInput
71
+ extend DumbInput
73
72
  else
74
73
  __input_error
75
74
  end