rcurses 5.1.0 → 5.1.1
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/lib/rcurses/pane.rb +121 -30
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8be17e6f76652103c068e4299b2c0bb1818ae74512aaff66e09362cdf10fecbf
|
4
|
+
data.tar.gz: 13ebe397820e760f3059bd6df1acfea80c1765e8c7ac66bc00caeb09786119f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dfb744f2464d2e4529180c15851e71ce5f033a7495f96e1f6f9bdaf1aaf7e4c6b788bd1164cdc7536a4ab740c3ac3e6348721712787ede4f09e021c958f5fffe
|
7
|
+
data.tar.gz: b54c0bf4e06484eed793bf07ed779427357b6635a7008e8ac12fe8318715631697cf7ddc8ca18f3aa71a73b8d327df369e5b670aed0582ede49a5ebef36e6dfe
|
data/lib/rcurses/pane.rb
CHANGED
@@ -9,7 +9,7 @@ module Rcurses
|
|
9
9
|
attr_accessor :record, :history
|
10
10
|
|
11
11
|
def initialize(x = 1, y = 1, w = 1, h = 1, fg = nil, bg = nil)
|
12
|
-
@max_h, @max_w = IO.console.winsize
|
12
|
+
@max_h, @max_w = IO.console ? IO.console.winsize : [24, 80]
|
13
13
|
@x = x
|
14
14
|
@y = y
|
15
15
|
@w = w
|
@@ -198,6 +198,10 @@ module Rcurses
|
|
198
198
|
STDOUT.print "\e[?25l\e[?7l\e[0m\e[r"
|
199
199
|
|
200
200
|
fmt = [@fg.to_s, @bg.to_s].join(',')
|
201
|
+
|
202
|
+
# Skip color application if fg and bg are both nil or empty
|
203
|
+
@skip_colors = (@fg.nil? && @bg.nil?) || (fmt == ",")
|
204
|
+
|
201
205
|
|
202
206
|
# Lazy evaluation: If the content or pane width has changed, reinitialize the lazy cache.
|
203
207
|
if !defined?(@cached_text) || @cached_text != cont || @cached_w != @w
|
@@ -251,16 +255,31 @@ module Rcurses
|
|
251
255
|
pl = @w - Rcurses.display_width(@txt[l].pure)
|
252
256
|
pl = 0 if pl < 0
|
253
257
|
hl = pl / 2
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
258
|
+
# Skip color application if pane has no colors set or text has ANY ANSI codes
|
259
|
+
if @skip_colors || @txt[l].include?("\e[")
|
260
|
+
# Don't apply pane colors - text already has ANSI sequences
|
261
|
+
case @align
|
262
|
+
when "l"
|
263
|
+
line_str = @txt[l] + " " * pl
|
264
|
+
when "r"
|
265
|
+
line_str = " " * pl + @txt[l]
|
266
|
+
when "c"
|
267
|
+
line_str = " " * hl + @txt[l] + " " * (pl - hl)
|
268
|
+
end
|
269
|
+
else
|
270
|
+
# Apply pane colors normally
|
271
|
+
case @align
|
272
|
+
when "l"
|
273
|
+
line_str = @txt[l].c(fmt) + " ".c(fmt) * pl
|
274
|
+
when "r"
|
275
|
+
line_str = " ".c(fmt) * pl + @txt[l].c(fmt)
|
276
|
+
when "c"
|
277
|
+
line_str = " ".c(fmt) * hl + @txt[l].c(fmt) + " ".c(fmt) * (pl - hl)
|
278
|
+
end
|
261
279
|
end
|
262
280
|
else
|
263
|
-
|
281
|
+
# Empty line - only apply colors if pane has them
|
282
|
+
line_str = @skip_colors ? " " * @w : " ".c(fmt) * @w
|
264
283
|
end
|
265
284
|
|
266
285
|
new_frame << line_str
|
@@ -278,6 +297,16 @@ module Rcurses
|
|
278
297
|
# restore wrap, then also reset SGR and scroll-region one more time
|
279
298
|
diff_buf << "\e[#{o_row};#{o_col}H\e[?7h\e[0m\e[r"
|
280
299
|
begin
|
300
|
+
# Debug: check what's actually being printed
|
301
|
+
if diff_buf.include?("Purpose") && diff_buf.include?("[38;5;")
|
302
|
+
File.open("/tmp/rcurses_debug.log", "a") do |f|
|
303
|
+
f.puts "=== PRINT DEBUG ==="
|
304
|
+
f.puts "diff_buf sample: #{diff_buf[0..200].inspect}"
|
305
|
+
f.puts "Has escape byte 27: #{diff_buf.bytes.include?(27)}"
|
306
|
+
f.puts "Escape count: #{diff_buf.bytes.count(27)}"
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
281
310
|
print diff_buf
|
282
311
|
rescue => e
|
283
312
|
# If printing fails, at least try to restore terminal state
|
@@ -640,34 +669,94 @@ module Rcurses
|
|
640
669
|
begin
|
641
670
|
return [""] if line.nil? || w <= 0
|
642
671
|
|
643
|
-
open_sequences = {
|
644
|
-
"\e[1m" => "\e[22m",
|
645
|
-
"\e[3m" => "\e[23m",
|
646
|
-
"\e[4m" => "\e[24m",
|
647
|
-
"\e[5m" => "\e[25m",
|
648
|
-
"\e[7m" => "\e[27m"
|
649
|
-
}
|
650
|
-
close_sequences = open_sequences.values + ["\e[0m"]
|
651
672
|
ansi_regex = /\e\[[0-9;]*m/
|
652
673
|
result = []
|
653
674
|
tokens = line.scan(/(\e\[[0-9;]*m|[^\e]+)/).flatten.compact
|
654
675
|
current_line = ''
|
655
676
|
current_line_length = 0
|
656
|
-
|
677
|
+
|
678
|
+
# Track SGR state properly
|
679
|
+
sgr_state = {
|
680
|
+
bold: false, # 1/22
|
681
|
+
italic: false, # 3/23
|
682
|
+
underline: false, # 4/24
|
683
|
+
blink: false, # 5/25
|
684
|
+
reverse: false, # 7/27
|
685
|
+
fg_color: nil, # 38/39 (nil means default)
|
686
|
+
bg_color: nil # 48/49 (nil means default)
|
687
|
+
}
|
688
|
+
|
689
|
+
# Helper to parse SGR parameters
|
690
|
+
parse_sgr = lambda do |sequence|
|
691
|
+
return unless sequence =~ /\e\[([0-9;]*)m/
|
692
|
+
param_str = $1
|
693
|
+
params = param_str.empty? ? [0] : param_str.split(';').map(&:to_i)
|
694
|
+
i = 0
|
695
|
+
while i < params.length
|
696
|
+
case params[i]
|
697
|
+
when 0 # Reset all
|
698
|
+
sgr_state[:bold] = false
|
699
|
+
sgr_state[:italic] = false
|
700
|
+
sgr_state[:underline] = false
|
701
|
+
sgr_state[:blink] = false
|
702
|
+
sgr_state[:reverse] = false
|
703
|
+
sgr_state[:fg_color] = nil
|
704
|
+
sgr_state[:bg_color] = nil
|
705
|
+
when 1 then sgr_state[:bold] = true
|
706
|
+
when 3 then sgr_state[:italic] = true
|
707
|
+
when 4 then sgr_state[:underline] = true
|
708
|
+
when 5 then sgr_state[:blink] = true
|
709
|
+
when 7 then sgr_state[:reverse] = true
|
710
|
+
when 22 then sgr_state[:bold] = false
|
711
|
+
when 23 then sgr_state[:italic] = false
|
712
|
+
when 24 then sgr_state[:underline] = false
|
713
|
+
when 25 then sgr_state[:blink] = false
|
714
|
+
when 27 then sgr_state[:reverse] = false
|
715
|
+
when 38 # Foreground color
|
716
|
+
if params[i+1] == 5 && params[i+2] # 256 color
|
717
|
+
sgr_state[:fg_color] = "38;5;#{params[i+2]}"
|
718
|
+
i += 2
|
719
|
+
elsif params[i+1] == 2 && params[i+4] # RGB color
|
720
|
+
sgr_state[:fg_color] = "38;2;#{params[i+2]};#{params[i+3]};#{params[i+4]}"
|
721
|
+
i += 4
|
722
|
+
end
|
723
|
+
when 39 then sgr_state[:fg_color] = nil # Default foreground
|
724
|
+
when 48 # Background color
|
725
|
+
if params[i+1] == 5 && params[i+2] # 256 color
|
726
|
+
sgr_state[:bg_color] = "48;5;#{params[i+2]}"
|
727
|
+
i += 2
|
728
|
+
elsif params[i+1] == 2 && params[i+4] # RGB color
|
729
|
+
sgr_state[:bg_color] = "48;2;#{params[i+2]};#{params[i+3]};#{params[i+4]}"
|
730
|
+
i += 4
|
731
|
+
end
|
732
|
+
when 49 then sgr_state[:bg_color] = nil # Default background
|
733
|
+
# Handle legacy 8-color codes (30-37, 40-47, 90-97, 100-107)
|
734
|
+
when 30..37 then sgr_state[:fg_color] = params[i].to_s
|
735
|
+
when 40..47 then sgr_state[:bg_color] = params[i].to_s
|
736
|
+
when 90..97 then sgr_state[:fg_color] = params[i].to_s
|
737
|
+
when 100..107 then sgr_state[:bg_color] = params[i].to_s
|
738
|
+
end
|
739
|
+
i += 1
|
740
|
+
end
|
741
|
+
end
|
742
|
+
|
743
|
+
# Helper to reconstruct SGR sequence from state
|
744
|
+
build_sgr = lambda do
|
745
|
+
codes = []
|
746
|
+
codes << "1" if sgr_state[:bold]
|
747
|
+
codes << "3" if sgr_state[:italic]
|
748
|
+
codes << "4" if sgr_state[:underline]
|
749
|
+
codes << "5" if sgr_state[:blink]
|
750
|
+
codes << "7" if sgr_state[:reverse]
|
751
|
+
codes << sgr_state[:fg_color] if sgr_state[:fg_color]
|
752
|
+
codes << sgr_state[:bg_color] if sgr_state[:bg_color]
|
753
|
+
codes.empty? ? "" : "\e[#{codes.join(';')}m"
|
754
|
+
end
|
657
755
|
|
658
756
|
tokens.each do |token|
|
659
757
|
if token.match?(ansi_regex)
|
660
758
|
current_line << token
|
661
|
-
|
662
|
-
if token == "\e[0m"
|
663
|
-
active_sequences.clear
|
664
|
-
else
|
665
|
-
corresponding_open = open_sequences.key(token)
|
666
|
-
active_sequences.delete(corresponding_open)
|
667
|
-
end
|
668
|
-
else
|
669
|
-
active_sequences << token
|
670
|
-
end
|
759
|
+
parse_sgr.call(token)
|
671
760
|
else
|
672
761
|
words = token.scan(/\s+|\S+/)
|
673
762
|
words.each do |word|
|
@@ -679,7 +768,8 @@ module Rcurses
|
|
679
768
|
else
|
680
769
|
if current_line_length > 0
|
681
770
|
result << current_line
|
682
|
-
|
771
|
+
# Start new line with current SGR state
|
772
|
+
current_line = build_sgr.call
|
683
773
|
current_line_length = 0
|
684
774
|
end
|
685
775
|
while word_length > w
|
@@ -688,7 +778,8 @@ module Rcurses
|
|
688
778
|
result << current_line
|
689
779
|
word = word[[w, word.length].min..-1] || ""
|
690
780
|
word_length = Rcurses.display_width(word.gsub(ansi_regex, ''))
|
691
|
-
|
781
|
+
# Start new line with current SGR state
|
782
|
+
current_line = build_sgr.call
|
692
783
|
current_line_length = 0
|
693
784
|
end
|
694
785
|
if word_length > 0
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rcurses
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.1.
|
4
|
+
version: 5.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Geir Isene
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-08-
|
11
|
+
date: 2025-08-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: clipboard
|