termchart 0.1.2 → 0.2.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: 61d7a54444a32d1b78ca78fc4d8b5d4e1c5c16b7e26a9f5a163066fd9ebc7831
4
- data.tar.gz: e5c466192416619cf7e556c5f8ad7cae6dd84210e5f5a2acfc58b33bc5f3b350
3
+ metadata.gz: 55f452dc8d64878667f8f2b1713082ffa6749c457094c35ee66e0b0defa622b5
4
+ data.tar.gz: b45f48358e95976f342890386a470eed2f9765ec2e6d06a43ebbe4345bf6b1c2
5
5
  SHA512:
6
- metadata.gz: 93b4a0c40d9dcc79d311040340714218d0d89c9215383bcab42de9b7c20b704870d3e394505753c17a2db978c08ca7c92cf724c4f2ed74202328bb03d0c989df
7
- data.tar.gz: 5e5805164766efd3d04089ed36aaaba435cecbf2574c0b7d232e3c0cbc1e65cca8733dc66355f6d22cd4018fb58093ee491b4adc1e791cfa72d4d6a9c17c8336
6
+ metadata.gz: 4c416cae0fdeeec726b42661b071ed3d1e53b87631c69005018c65d865954446b5698eb9c48abd4d7c8d64861738d63eee2e661536c9c67979810bd92e74ca88
7
+ data.tar.gz: bdb901634be7a9ee63e975d3d21460a279b4e3e3e839eebdac060ceffde4d2d1d971d1e98ca42b88576bf13a0b6172be41670e1e6d8f827603d4ff0fab33e326
data/lib/termchart/bar.rb CHANGED
@@ -21,6 +21,9 @@ module Termchart
21
21
 
22
22
  def render
23
23
  return "" if @items.empty?
24
+ if @items.any? { |i| i[:value] < 0 }
25
+ raise ArgumentError, "Bar chart does not support negative values"
26
+ end
24
27
  @orientation == :horizontal ? render_horizontal : render_vertical
25
28
  end
26
29
 
@@ -43,7 +46,7 @@ module Termchart
43
46
  pad = bar_w - visible_len(bar)
44
47
  pad = 0 if pad < 0
45
48
  bar_str = bar + " " * pad
46
- bar_str = colorize(bar_str, item[:color]) if item[:color]
49
+ bar_str = Termchart.colorize(bar_str, item[:color]) if item[:color]
47
50
  lbl = item[:label].ljust(label_w)
48
51
  val = format_val(item[:value]).rjust(val_w)
49
52
  "#{lbl} │#{bar_str} #{val}"
@@ -78,7 +81,7 @@ module Termchart
78
81
  h.times do |row|
79
82
  parts = cols.each_with_index.map do |col, i|
80
83
  cell = col[row]
81
- @items[i][:color] ? colorize(cell, @items[i][:color]) : cell
84
+ @items[i][:color] ? Termchart.colorize(cell, @items[i][:color]) : cell
82
85
  end
83
86
  lines << parts.join(" ")
84
87
  end
@@ -96,15 +99,5 @@ module Termchart
96
99
  str.gsub(/\e\[[0-9;]*m/, "").length
97
100
  end
98
101
 
99
- def colorize(str, color)
100
- code = case color
101
- when Integer then "38;5;#{color}"
102
- when /\A#?([0-9a-fA-F]{6})\z/
103
- r, g, b = [$1].pack("H*").unpack("CCC")
104
- "38;2;#{r};#{g};#{b}"
105
- else "38;5;#{color}"
106
- end
107
- "\e[#{code}m#{str}\e[0m"
108
- end
109
102
  end
110
103
  end
@@ -17,15 +17,15 @@ module Termchart
17
17
  # Add OHLC data: array of hashes with keys :o, :h, :l, :c
18
18
  def add(ohlc_data)
19
19
  ohlc_data.each do |d|
20
- @data << {
21
- o: d[:o].to_f, h: d[:h].to_f,
22
- l: d[:l].to_f, c: d[:c].to_f
23
- }
20
+ o, h, l, c = d[:o].to_f, d[:h].to_f, d[:l].to_f, d[:c].to_f
21
+ raise ArgumentError, "high (#{h}) must be >= low (#{l})" if h < l
22
+ @data << { o: o, h: h, l: l, c: c }
24
23
  end
25
24
  end
26
25
 
27
26
  def render
28
27
  return "" if @data.empty?
28
+ effective_height = [@height, 3].max
29
29
 
30
30
  data_min = @data.map { |d| d[:l] }.min
31
31
  data_max = @data.map { |d| d[:h] }.max
@@ -33,10 +33,10 @@ module Termchart
33
33
  data_range = 1.0 if data_range.zero?
34
34
 
35
35
  # Y-axis label width
36
- y_label_w = [format_num(data_max).length, format_num(data_min).length].max + 1
36
+ y_label_w = [Termchart.format_num(data_max).length, Termchart.format_num(data_min).length].max + 1
37
37
  chart_w = @width - y_label_w
38
38
  chart_w = 10 if chart_w < 10
39
- chart_h = @height
39
+ chart_h = effective_height
40
40
 
41
41
  # Decide how many candles to show (1 char width each, with gaps)
42
42
  max_candles = chart_w / 2 # each candle takes 1 col + 1 gap
@@ -89,18 +89,18 @@ module Termchart
89
89
  # Add Y-axis labels
90
90
  result = lines.each_with_index.map do |line, row|
91
91
  if row == 0
92
- label = format_num(data_max).rjust(y_label_w - 1) + "┤"
92
+ label = Termchart.format_num(data_max).rjust(y_label_w - 1) + "┤"
93
93
  elsif row == chart_h - 1
94
- label = format_num(data_min).rjust(y_label_w - 1) + "┤"
94
+ label = Termchart.format_num(data_min).rjust(y_label_w - 1) + "┤"
95
95
  elsif row == chart_h / 2
96
96
  mid = data_min + data_range / 2.0
97
- label = format_num(mid).rjust(y_label_w - 1) + "┤"
97
+ label = Termchart.format_num(mid).rjust(y_label_w - 1) + "┤"
98
98
  elsif row == chart_h / 4
99
99
  q1 = data_max - data_range / 4.0
100
- label = format_num(q1).rjust(y_label_w - 1) + "┤"
100
+ label = Termchart.format_num(q1).rjust(y_label_w - 1) + "┤"
101
101
  elsif row == chart_h * 3 / 4
102
102
  q3 = data_min + data_range / 4.0
103
- label = format_num(q3).rjust(y_label_w - 1) + "┤"
103
+ label = Termchart.format_num(q3).rjust(y_label_w - 1) + "┤"
104
104
  else
105
105
  label = " " * (y_label_w - 1) + "│"
106
106
  end
@@ -110,16 +110,5 @@ module Termchart
110
110
  result.join("\n")
111
111
  end
112
112
 
113
- private
114
-
115
- def format_num(v)
116
- if v.abs >= 1000
117
- "%.0f" % v
118
- elsif v.abs >= 100
119
- "%.1f" % v
120
- else
121
- "%.2f" % v
122
- end
123
- end
124
113
  end
125
114
  end
@@ -17,6 +17,7 @@ module Termchart
17
17
 
18
18
  def render
19
19
  return "" if @series.empty?
20
+ effective_height = [@height, 3].max
20
21
 
21
22
  # Compute global min/max across all series
22
23
  all_vals = @series.flat_map { |s| s[:values] }
@@ -27,10 +28,10 @@ module Termchart
27
28
 
28
29
  # Reserve space for Y-axis labels (use widest of min/mid/max)
29
30
  mid = data_min + data_range / 2.0
30
- y_label_w = [format_num(data_max), format_num(data_min), format_num(mid)].map(&:length).max + 1
31
+ y_label_w = [Termchart.format_num(data_max), Termchart.format_num(data_min), Termchart.format_num(mid)].map(&:length).max + 1
31
32
  chart_w = @width - y_label_w
32
33
  chart_w = 10 if chart_w < 10
33
- chart_h = @height
34
+ chart_h = effective_height
34
35
 
35
36
  # Pixel dimensions (braille: 2 dots wide, 4 dots tall per cell)
36
37
  px_w = chart_w * 2
@@ -66,12 +67,12 @@ module Termchart
66
67
  lines = chart_str.split("\n")
67
68
  result = lines.each_with_index.map do |line, row|
68
69
  if row == 0
69
- label = format_num(data_max).rjust(y_label_w - 1) + "┤"
70
+ label = Termchart.format_num(data_max).rjust(y_label_w - 1) + "┤"
70
71
  elsif row == chart_h - 1
71
- label = format_num(data_min).rjust(y_label_w - 1) + "┤"
72
+ label = Termchart.format_num(data_min).rjust(y_label_w - 1) + "┤"
72
73
  elsif row == chart_h / 2
73
74
  mid = data_min + data_range / 2.0
74
- label = format_num(mid).rjust(y_label_w - 1) + "┤"
75
+ label = Termchart.format_num(mid).rjust(y_label_w - 1) + "┤"
75
76
  else
76
77
  label = " " * (y_label_w - 1) + "│"
77
78
  end
@@ -82,7 +83,7 @@ module Termchart
82
83
  if @series.any? { |s| s[:label] }
83
84
  legend = @series.map do |s|
84
85
  name = s[:label] || "data"
85
- colorize("━━", s[:color]) + " " + name
86
+ Termchart.colorize("━━", s[:color]) + " " + name
86
87
  end.join(" ")
87
88
  result << " " * y_label_w + legend
88
89
  end
@@ -113,25 +114,5 @@ module Termchart
113
114
  end
114
115
  end
115
116
 
116
- def format_num(v)
117
- if v.abs >= 1000
118
- "%.0f" % v
119
- elsif v.abs >= 1
120
- "%.1f" % v
121
- else
122
- "%.2f" % v
123
- end
124
- end
125
-
126
- def colorize(str, color)
127
- code = case color
128
- when Integer then "38;5;#{color}"
129
- when /\A#?([0-9a-fA-F]{6})\z/
130
- r, g, b = [$1].pack("H*").unpack("CCC")
131
- "38;2;#{r};#{g};#{b}"
132
- else "38;5;#{color}"
133
- end
134
- "\e[#{code}m#{str}\e[0m"
135
- end
136
117
  end
137
118
  end
@@ -15,26 +15,7 @@ module Termchart
15
15
  TICKS[idx]
16
16
  end
17
17
  line = chars.join
18
- color ? colorize(line, color) : line
18
+ color ? Termchart.colorize(line, color) : line
19
19
  end
20
-
21
- private
22
-
23
- def self.colorize(str, color)
24
- code = case color
25
- when Integer then "38;5;#{color}"
26
- when Symbol then "38;5;#{NAMED_COLORS[color] || 7}"
27
- when /\A#?([0-9a-fA-F]{6})\z/
28
- r, g, b = [$1].pack("H*").unpack("CCC")
29
- "38;2;#{r};#{g};#{b}"
30
- else "38;5;#{color}"
31
- end
32
- "\e[#{code}m#{str}\e[0m"
33
- end
34
-
35
- NAMED_COLORS = {
36
- red: 196, green: 82, blue: 33, yellow: 226, cyan: 51,
37
- magenta: 201, white: 255, gray: 245, orange: 208
38
- }.freeze
39
20
  end
40
21
  end
@@ -1,3 +1,3 @@
1
1
  module Termchart
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/termchart.rb CHANGED
@@ -6,6 +6,38 @@ require_relative "termchart/candle"
6
6
  require_relative "termchart/bar"
7
7
 
8
8
  module Termchart
9
+ NAMED_COLORS = {
10
+ red: 196, green: 82, blue: 33, yellow: 226, cyan: 51,
11
+ magenta: 201, white: 255, gray: 245, orange: 208
12
+ }.freeze
13
+
14
+ # Shared colorize helper (ANSI escape wrapping).
15
+ # Supports Integer (256-color), Symbol (named), and hex string.
16
+ def self.colorize(str, color)
17
+ code = case color
18
+ when Integer then "38;5;#{color}"
19
+ when Symbol then "38;5;#{NAMED_COLORS[color] || 7}"
20
+ when /\A#?([0-9a-fA-F]{6})\z/
21
+ r, g, b = [$1].pack("H*").unpack("CCC")
22
+ "38;2;#{r};#{g};#{b}"
23
+ else "38;5;#{color}"
24
+ end
25
+ "\e[#{code}m#{str}\e[0m"
26
+ end
27
+
28
+ # Shared numeric formatter for axis labels.
29
+ def self.format_num(v)
30
+ if v.abs >= 1000
31
+ "%.0f" % v
32
+ elsif v.abs >= 100
33
+ "%.1f" % v
34
+ elsif v.abs >= 1
35
+ "%.1f" % v
36
+ else
37
+ "%.2f" % v
38
+ end
39
+ end
40
+
9
41
  # Convenience: Termchart.spark([1,3,5,2,8], color: :green)
10
42
  def self.spark(values, color: nil)
11
43
  Spark.render(values, color: color)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: termchart
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geir Isene
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-18 00:00:00.000000000 Z
11
+ date: 2026-03-21 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Render sparklines, line charts (braille), candlestick charts, and bar
14
14
  charts as plain strings with ANSI color codes. Zero dependencies, pure Ruby.