ratatui_ruby 0.1.0 → 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 +4 -4
- data/.builds/ruby-3.2.yml +52 -0
- data/.builds/ruby-3.3.yml +52 -0
- data/.builds/ruby-3.4.yml +52 -0
- data/.builds/ruby-4.0.0-preview3.yml +53 -0
- data/AGENTS.md +2 -2
- data/README.md +33 -13
- data/REUSE.toml +5 -0
- data/Rakefile +3 -100
- data/docs/images/examples-calendar_demo.rb.png +0 -0
- data/docs/images/examples-chart_demo.rb.png +0 -0
- data/docs/images/examples-list_styles.rb.png +0 -0
- data/docs/images/examples-quickstart_lifecycle.rb.png +0 -0
- data/docs/images/examples-stock_ticker.rb.png +0 -0
- data/docs/quickstart.md +57 -11
- data/examples/analytics.rb +2 -1
- data/examples/calendar_demo.rb +55 -0
- data/examples/chart_demo.rb +84 -0
- data/examples/list_styles.rb +66 -0
- data/examples/login_form.rb +2 -1
- data/examples/quickstart_dsl.rb +30 -0
- data/examples/quickstart_lifecycle.rb +40 -0
- data/examples/readme_usage.rb +21 -0
- data/examples/stock_ticker.rb +13 -5
- data/examples/system_monitor.rb +2 -1
- data/examples/test_calendar_demo.rb +66 -0
- data/examples/test_list_styles.rb +61 -0
- data/ext/ratatui_ruby/.cargo/config.toml +5 -0
- data/ext/ratatui_ruby/Cargo.lock +94 -1
- data/ext/ratatui_ruby/Cargo.toml +3 -2
- data/ext/ratatui_ruby/extconf.rb +1 -1
- data/ext/ratatui_ruby/src/events.rs +4 -1
- data/ext/ratatui_ruby/src/rendering.rs +4 -1
- data/ext/ratatui_ruby/src/terminal.rs +4 -6
- data/ext/ratatui_ruby/src/widgets/calendar.rs +81 -0
- data/ext/ratatui_ruby/src/widgets/chart.rs +253 -0
- data/ext/ratatui_ruby/src/widgets/list.rs +41 -4
- data/ext/ratatui_ruby/src/widgets/mod.rs +2 -1
- data/lib/ratatui_ruby/dsl.rb +62 -0
- data/lib/ratatui_ruby/schema/calendar.rb +26 -0
- data/lib/ratatui_ruby/schema/chart.rb +81 -0
- data/lib/ratatui_ruby/schema/list.rb +8 -2
- data/lib/ratatui_ruby/version.rb +1 -1
- data/lib/ratatui_ruby.rb +21 -1
- data/mise.toml +8 -0
- data/sig/ratatui_ruby/schema/calendar.rbs +13 -0
- data/sig/ratatui_ruby/schema/{line_chart.rbs → chart.rbs} +20 -1
- data/sig/ratatui_ruby/schema/list.rbs +4 -1
- data/tasks/bump/cargo_lockfile.rb +19 -0
- data/tasks/bump/manifest.rb +23 -0
- data/tasks/bump/ruby_gem.rb +39 -0
- data/tasks/bump/sem_ver.rb +28 -0
- data/tasks/bump.rake +45 -0
- data/tasks/doc.rake +24 -0
- data/tasks/extension.rake +12 -0
- data/tasks/lint.rake +49 -0
- data/tasks/rdoc_config.rb +15 -0
- data/tasks/resources/build.yml.erb +65 -0
- data/tasks/resources/index.html.erb +38 -0
- data/tasks/resources/rubies.yml +7 -0
- data/tasks/sourcehut.rake +29 -0
- data/tasks/test.rake +31 -0
- data/tasks/website/index_page.rb +28 -0
- data/tasks/website/version.rb +116 -0
- data/tasks/website/versioned_documentation.rb +48 -0
- data/tasks/website/website.rb +50 -0
- data/tasks/website.rake +26 -0
- metadata +51 -10
- data/.build.yml +0 -34
- data/.ruby-version +0 -1
- data/ext/ratatui_ruby/src/widgets/linechart.rs +0 -154
- data/lib/ratatui_ruby/schema/line_chart.rb +0 -41
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
7
|
+
require "ratatui_ruby"
|
|
8
|
+
|
|
9
|
+
# Chart Demo
|
|
10
|
+
# Demonstrates Scatter and Line datasets in a single Chart.
|
|
11
|
+
class ChartDemoApp
|
|
12
|
+
def run
|
|
13
|
+
RatatuiRuby.init_terminal
|
|
14
|
+
begin
|
|
15
|
+
loop do
|
|
16
|
+
render
|
|
17
|
+
break if handle_input == :quit
|
|
18
|
+
sleep 0.1
|
|
19
|
+
end
|
|
20
|
+
ensure
|
|
21
|
+
RatatuiRuby.restore_terminal
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def render
|
|
26
|
+
# Scatter data: Random points
|
|
27
|
+
scatter_data = Array.new(20) { [rand(0.0..10.0), rand(-1.0..1.0)] }
|
|
28
|
+
|
|
29
|
+
# Line data: Sine wave
|
|
30
|
+
line_data = (0..100).map do |i|
|
|
31
|
+
x = i / 10.0
|
|
32
|
+
[x, Math.sin(x)]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
datasets = [
|
|
36
|
+
RatatuiRuby::Dataset.new(
|
|
37
|
+
name: "Scatter",
|
|
38
|
+
data: scatter_data,
|
|
39
|
+
color: "red",
|
|
40
|
+
marker: :dot,
|
|
41
|
+
graph_type: :scatter,
|
|
42
|
+
),
|
|
43
|
+
RatatuiRuby::Dataset.new(
|
|
44
|
+
name: "Line",
|
|
45
|
+
data: line_data,
|
|
46
|
+
color: "green",
|
|
47
|
+
marker: :braille,
|
|
48
|
+
graph_type: :line,
|
|
49
|
+
),
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
chart = RatatuiRuby::Chart.new(
|
|
53
|
+
datasets:,
|
|
54
|
+
x_axis: RatatuiRuby::Axis.new(
|
|
55
|
+
title: "Time",
|
|
56
|
+
bounds: [0.0, 10.0],
|
|
57
|
+
labels: %w[0 5 10],
|
|
58
|
+
style: RatatuiRuby::Style.new(fg: :yellow),
|
|
59
|
+
),
|
|
60
|
+
y_axis: RatatuiRuby::Axis.new(
|
|
61
|
+
title: "Amplitude",
|
|
62
|
+
bounds: [-1.0, 1.0],
|
|
63
|
+
labels: %w[-1 0 1],
|
|
64
|
+
style: RatatuiRuby::Style.new(fg: :cyan),
|
|
65
|
+
),
|
|
66
|
+
block: RatatuiRuby::Block.new(
|
|
67
|
+
title: "Chart Demo (Q to quit)",
|
|
68
|
+
borders: [:all],
|
|
69
|
+
),
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
RatatuiRuby.draw(chart)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def handle_input
|
|
76
|
+
event = RatatuiRuby.poll_event
|
|
77
|
+
return nil unless event
|
|
78
|
+
|
|
79
|
+
return :quit if event[:type] == :key && event[:code] == "q"
|
|
80
|
+
nil
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
ChartDemoApp.new.run if __FILE__ == $PROGRAM_NAME
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
7
|
+
require "ratatui_ruby"
|
|
8
|
+
|
|
9
|
+
# Styled List Example
|
|
10
|
+
# Demonstrates advanced styling options for the List widget.
|
|
11
|
+
class ListStylesApp
|
|
12
|
+
attr_reader :selected_index
|
|
13
|
+
|
|
14
|
+
def initialize
|
|
15
|
+
@items = ["Item 1", "Item 2", "Item 3"]
|
|
16
|
+
@selected_index = 0
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def run
|
|
20
|
+
RatatuiRuby.init_terminal
|
|
21
|
+
begin
|
|
22
|
+
loop do
|
|
23
|
+
render
|
|
24
|
+
break if handle_input == :quit
|
|
25
|
+
sleep 0.05
|
|
26
|
+
end
|
|
27
|
+
ensure
|
|
28
|
+
RatatuiRuby.restore_terminal
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def render
|
|
33
|
+
RatatuiRuby.draw(
|
|
34
|
+
RatatuiRuby::List.new(
|
|
35
|
+
items: @items,
|
|
36
|
+
selected_index: @selected_index,
|
|
37
|
+
style: RatatuiRuby::Style.new(fg: :white, bg: :black),
|
|
38
|
+
highlight_style: RatatuiRuby::Style.new(fg: :blue, bg: :white, modifiers: [:bold]),
|
|
39
|
+
highlight_symbol: ">> ",
|
|
40
|
+
block: RatatuiRuby::Block.new(
|
|
41
|
+
title: "Styled List (Up/Down to move, Q to quit)",
|
|
42
|
+
borders: [:all]
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def handle_input
|
|
49
|
+
event = RatatuiRuby.poll_event
|
|
50
|
+
return nil unless event
|
|
51
|
+
|
|
52
|
+
if event[:type] == :key
|
|
53
|
+
case event[:code]
|
|
54
|
+
when "up"
|
|
55
|
+
@selected_index = (@selected_index - 1) % @items.size
|
|
56
|
+
when "down"
|
|
57
|
+
@selected_index = (@selected_index + 1) % @items.size
|
|
58
|
+
when "q"
|
|
59
|
+
return :quit
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
nil
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
ListStylesApp.new.run if __FILE__ == $PROGRAM_NAME
|
data/examples/login_form.rb
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
4
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
7
|
+
require "ratatui_ruby"
|
|
7
8
|
|
|
8
9
|
class LoginFormApp
|
|
9
10
|
PREFIX = "Enter Username: [ "
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
7
|
+
|
|
8
|
+
require "ratatui_ruby"
|
|
9
|
+
|
|
10
|
+
# 1. Initialize the terminal, start the main loop, and ensure the terminal is restored.
|
|
11
|
+
RatatuiRuby.main_loop do |tui|
|
|
12
|
+
# 2. Create your UI with methods instead of classes.
|
|
13
|
+
view = tui.paragraph(
|
|
14
|
+
text: "Hello, Ratatui! Press 'q' to quit.",
|
|
15
|
+
align: :center,
|
|
16
|
+
block: tui.block(
|
|
17
|
+
title: "My Ruby TUI App",
|
|
18
|
+
borders: [:all],
|
|
19
|
+
border_color: "cyan"
|
|
20
|
+
)
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# 3. Use RatatuiRuby methods, too.
|
|
24
|
+
tui.draw(view)
|
|
25
|
+
event = tui.poll_event
|
|
26
|
+
|
|
27
|
+
if event && event[:type] == :key && event[:code] == "q"
|
|
28
|
+
break
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
7
|
+
|
|
8
|
+
require "ratatui_ruby"
|
|
9
|
+
|
|
10
|
+
# 1. Initialize the terminal
|
|
11
|
+
RatatuiRuby.init_terminal
|
|
12
|
+
|
|
13
|
+
begin
|
|
14
|
+
# The Main Loop
|
|
15
|
+
loop do
|
|
16
|
+
# 2. Create your UI (Immediate Mode)
|
|
17
|
+
# We define a Paragraph widget inside a Block with a title and borders.
|
|
18
|
+
view = RatatuiRuby::Paragraph.new(
|
|
19
|
+
text: "Hello, Ratatui! Press 'q' to quit.",
|
|
20
|
+
align: :center,
|
|
21
|
+
block: RatatuiRuby::Block.new(
|
|
22
|
+
title: "My Ruby TUI App",
|
|
23
|
+
borders: [:all],
|
|
24
|
+
border_color: "cyan"
|
|
25
|
+
)
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# 3. Draw the UI
|
|
29
|
+
RatatuiRuby.draw(view)
|
|
30
|
+
|
|
31
|
+
# 4. Poll for events
|
|
32
|
+
event = RatatuiRuby.poll_event
|
|
33
|
+
if event && event[:type] == :key && event[:code] == "q"
|
|
34
|
+
break
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
ensure
|
|
38
|
+
# 5. Restore the terminal to its original state
|
|
39
|
+
RatatuiRuby.restore_terminal
|
|
40
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
7
|
+
|
|
8
|
+
require "ratatui_ruby"
|
|
9
|
+
RatatuiRuby.main_loop do |tui|
|
|
10
|
+
tui.draw \
|
|
11
|
+
tui.paragraph \
|
|
12
|
+
text: "Hello, Ratatui! Press 'q' to quit.",
|
|
13
|
+
align: :center,
|
|
14
|
+
block: tui.block(
|
|
15
|
+
title: "My Ruby TUI App",
|
|
16
|
+
borders: [:all],
|
|
17
|
+
border_color: "cyan"
|
|
18
|
+
)
|
|
19
|
+
event = tui.poll_event
|
|
20
|
+
break if event && event[:type] == :key && event[:code] == "q"
|
|
21
|
+
end
|
data/examples/stock_ticker.rb
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
4
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
7
|
+
require "ratatui_ruby"
|
|
7
8
|
|
|
8
9
|
# Simulate a stock ticker
|
|
9
10
|
class StockTickerApp
|
|
@@ -58,14 +59,21 @@ class StockTickerApp
|
|
|
58
59
|
style: RatatuiRuby::Style.new(fg: :cyan),
|
|
59
60
|
block: RatatuiRuby::Block.new(title: "Network Activity", borders: :all)
|
|
60
61
|
),
|
|
61
|
-
RatatuiRuby::
|
|
62
|
+
RatatuiRuby::Chart.new(
|
|
62
63
|
datasets: [
|
|
63
64
|
RatatuiRuby::Dataset.new(name: "RBY", data: @ruby_stock, color: :green),
|
|
64
65
|
RatatuiRuby::Dataset.new(name: "RST", data: @rust_stock, color: :red),
|
|
65
66
|
],
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
x_axis: RatatuiRuby::Axis.new(
|
|
68
|
+
title: "Time",
|
|
69
|
+
bounds: [((@counter > 100) ? @counter - 100.0 : 0.0), @counter.to_f],
|
|
70
|
+
labels: [@counter.to_s]
|
|
71
|
+
),
|
|
72
|
+
y_axis: RatatuiRuby::Axis.new(
|
|
73
|
+
title: "Price",
|
|
74
|
+
bounds: [0.0, 100.0],
|
|
75
|
+
labels: %w[0 50 100]
|
|
76
|
+
),
|
|
69
77
|
block: RatatuiRuby::Block.new(title: "Stock Ticker", borders: :all),
|
|
70
78
|
),
|
|
71
79
|
]
|
data/examples/system_monitor.rb
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
4
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
7
|
+
require "ratatui_ruby"
|
|
7
8
|
|
|
8
9
|
class SystemMonitorApp
|
|
9
10
|
def initialize
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
require "minitest/autorun"
|
|
7
|
+
require "ratatui_ruby"
|
|
8
|
+
|
|
9
|
+
module CalendarDemo
|
|
10
|
+
class TestCalendarDemo < Minitest::Test
|
|
11
|
+
def setup
|
|
12
|
+
RatatuiRuby.init_test_terminal(40, 20)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def test_demo_renders
|
|
16
|
+
# We can't easily test the loop in run, but we can test the rendering logic
|
|
17
|
+
now = Time.now
|
|
18
|
+
calendar = RatatuiRuby::Calendar.new(
|
|
19
|
+
year: now.year,
|
|
20
|
+
month: now.month,
|
|
21
|
+
header_style: RatatuiRuby::Style.new(fg: "yellow", modifiers: [:bold]),
|
|
22
|
+
block: RatatuiRuby::Block.new(title: " Calendar (q = quit) ", borders: [:all]),
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# Constrain the calendar to 24x10 characters
|
|
26
|
+
view = RatatuiRuby::Layout.new(
|
|
27
|
+
direction: :vertical,
|
|
28
|
+
constraints: [
|
|
29
|
+
RatatuiRuby::Constraint.length(10),
|
|
30
|
+
RatatuiRuby::Constraint.min(0),
|
|
31
|
+
],
|
|
32
|
+
children: [
|
|
33
|
+
RatatuiRuby::Layout.new(
|
|
34
|
+
direction: :horizontal,
|
|
35
|
+
constraints: [
|
|
36
|
+
RatatuiRuby::Constraint.length(24),
|
|
37
|
+
RatatuiRuby::Constraint.min(0),
|
|
38
|
+
],
|
|
39
|
+
children: [calendar, nil],
|
|
40
|
+
),
|
|
41
|
+
nil,
|
|
42
|
+
],
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
RatatuiRuby.draw(view)
|
|
46
|
+
content = RatatuiRuby.get_buffer_content
|
|
47
|
+
|
|
48
|
+
assert_match(/Calendar \(q = quit\)/, content)
|
|
49
|
+
assert_match(/#{now.year}/, content)
|
|
50
|
+
|
|
51
|
+
# Verify the size constraint: the terminal is 40x20
|
|
52
|
+
# But the layout should be constrained to 24x10.
|
|
53
|
+
lines = content.split("\n")
|
|
54
|
+
|
|
55
|
+
# First 10 rows: first 24 chars can have content, 24..39 should be empty
|
|
56
|
+
10.times do |i|
|
|
57
|
+
assert_equal " " * 16, lines[i][24..39], "Row #{i} should be empty after column 24"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Rows 10..19 should be completely empty
|
|
61
|
+
(10..19).each do |i|
|
|
62
|
+
assert_equal " " * 40, lines[i], "Row #{i} should be completely empty"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
7
|
+
require "ratatui_ruby"
|
|
8
|
+
require "ratatui_ruby/test_helper"
|
|
9
|
+
require "minitest/autorun"
|
|
10
|
+
require_relative "list_styles"
|
|
11
|
+
|
|
12
|
+
class Minitest::Test
|
|
13
|
+
include RatatuiRuby::TestHelper
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class TestListStyles < Minitest::Test
|
|
17
|
+
def setup
|
|
18
|
+
@app = ListStylesApp.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_render_initial_state
|
|
22
|
+
with_test_terminal(50, 20) do
|
|
23
|
+
@app.render
|
|
24
|
+
|
|
25
|
+
assert buffer_content.any? { |line| line.include?(">> Item 1") }
|
|
26
|
+
assert buffer_content.any? { |line| line.include?(" Item 2") }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_navigation_down
|
|
31
|
+
inject_event("key", { code: "down" })
|
|
32
|
+
@app.handle_input
|
|
33
|
+
|
|
34
|
+
with_test_terminal(50, 20) do
|
|
35
|
+
@app.render
|
|
36
|
+
assert buffer_content.any? { |line| line.include?(" Item 1") }
|
|
37
|
+
assert buffer_content.any? { |line| line.include?(">> Item 2") }
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_navigation_up
|
|
42
|
+
# Move down to Item 2
|
|
43
|
+
inject_event("key", { code: "down" })
|
|
44
|
+
@app.handle_input
|
|
45
|
+
|
|
46
|
+
# Move up back to Item 1
|
|
47
|
+
inject_event("key", { code: "up" })
|
|
48
|
+
@app.handle_input
|
|
49
|
+
|
|
50
|
+
with_test_terminal(50, 20) do
|
|
51
|
+
@app.render
|
|
52
|
+
assert buffer_content.any? { |line| line.include?(">> Item 1") }
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def test_quit
|
|
57
|
+
inject_event("key", { code: "q" })
|
|
58
|
+
status = @app.handle_input
|
|
59
|
+
assert_equal :quit, status
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -6,3 +6,8 @@ rustflags = ["-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup"]
|
|
|
6
6
|
|
|
7
7
|
[target.x86_64-apple-darwin]
|
|
8
8
|
rustflags = ["-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup"]
|
|
9
|
+
|
|
10
|
+
# Force dynamic linking on Alpine (musl) to fix "Dynamic loading not supported"
|
|
11
|
+
# errors when bindgen tries to load libclang.
|
|
12
|
+
[target.'cfg(target_env = "musl")']
|
|
13
|
+
rustflags = ["-C", "target-feature=-crt-static"]
|
data/ext/ratatui_ruby/Cargo.lock
CHANGED
|
@@ -122,6 +122,15 @@ dependencies = [
|
|
|
122
122
|
"winapi",
|
|
123
123
|
]
|
|
124
124
|
|
|
125
|
+
[[package]]
|
|
126
|
+
name = "deranged"
|
|
127
|
+
version = "0.5.5"
|
|
128
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
129
|
+
checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587"
|
|
130
|
+
dependencies = [
|
|
131
|
+
"powerfmt",
|
|
132
|
+
]
|
|
133
|
+
|
|
125
134
|
[[package]]
|
|
126
135
|
name = "either"
|
|
127
136
|
version = "1.15.0"
|
|
@@ -296,6 +305,21 @@ dependencies = [
|
|
|
296
305
|
"minimal-lexical",
|
|
297
306
|
]
|
|
298
307
|
|
|
308
|
+
[[package]]
|
|
309
|
+
name = "num-conv"
|
|
310
|
+
version = "0.1.0"
|
|
311
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
312
|
+
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
|
313
|
+
|
|
314
|
+
[[package]]
|
|
315
|
+
name = "num_threads"
|
|
316
|
+
version = "0.1.7"
|
|
317
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
318
|
+
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
|
|
319
|
+
dependencies = [
|
|
320
|
+
"libc",
|
|
321
|
+
]
|
|
322
|
+
|
|
299
323
|
[[package]]
|
|
300
324
|
name = "parking_lot"
|
|
301
325
|
version = "0.12.5"
|
|
@@ -325,6 +349,12 @@ version = "1.0.15"
|
|
|
325
349
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
326
350
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
|
327
351
|
|
|
352
|
+
[[package]]
|
|
353
|
+
name = "powerfmt"
|
|
354
|
+
version = "0.2.0"
|
|
355
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
356
|
+
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
|
357
|
+
|
|
328
358
|
[[package]]
|
|
329
359
|
name = "proc-macro2"
|
|
330
360
|
version = "1.0.103"
|
|
@@ -358,6 +388,7 @@ dependencies = [
|
|
|
358
388
|
"paste",
|
|
359
389
|
"stability",
|
|
360
390
|
"strum",
|
|
391
|
+
"time",
|
|
361
392
|
"unicode-segmentation",
|
|
362
393
|
"unicode-truncate",
|
|
363
394
|
"unicode-width",
|
|
@@ -365,12 +396,13 @@ dependencies = [
|
|
|
365
396
|
|
|
366
397
|
[[package]]
|
|
367
398
|
name = "ratatui_ruby"
|
|
368
|
-
version = "0.
|
|
399
|
+
version = "0.2.0"
|
|
369
400
|
dependencies = [
|
|
370
401
|
"crossterm",
|
|
371
402
|
"lazy_static",
|
|
372
403
|
"magnus",
|
|
373
404
|
"ratatui",
|
|
405
|
+
"time",
|
|
374
406
|
]
|
|
375
407
|
|
|
376
408
|
[[package]]
|
|
@@ -471,6 +503,35 @@ version = "0.3.6"
|
|
|
471
503
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
472
504
|
checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"
|
|
473
505
|
|
|
506
|
+
[[package]]
|
|
507
|
+
name = "serde"
|
|
508
|
+
version = "1.0.228"
|
|
509
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
510
|
+
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
|
511
|
+
dependencies = [
|
|
512
|
+
"serde_core",
|
|
513
|
+
]
|
|
514
|
+
|
|
515
|
+
[[package]]
|
|
516
|
+
name = "serde_core"
|
|
517
|
+
version = "1.0.228"
|
|
518
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
519
|
+
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
|
520
|
+
dependencies = [
|
|
521
|
+
"serde_derive",
|
|
522
|
+
]
|
|
523
|
+
|
|
524
|
+
[[package]]
|
|
525
|
+
name = "serde_derive"
|
|
526
|
+
version = "1.0.228"
|
|
527
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
528
|
+
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
|
529
|
+
dependencies = [
|
|
530
|
+
"proc-macro2",
|
|
531
|
+
"quote",
|
|
532
|
+
"syn",
|
|
533
|
+
]
|
|
534
|
+
|
|
474
535
|
[[package]]
|
|
475
536
|
name = "shell-words"
|
|
476
537
|
version = "1.1.1"
|
|
@@ -568,6 +629,38 @@ dependencies = [
|
|
|
568
629
|
"unicode-ident",
|
|
569
630
|
]
|
|
570
631
|
|
|
632
|
+
[[package]]
|
|
633
|
+
name = "time"
|
|
634
|
+
version = "0.3.44"
|
|
635
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
636
|
+
checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
|
|
637
|
+
dependencies = [
|
|
638
|
+
"deranged",
|
|
639
|
+
"libc",
|
|
640
|
+
"num-conv",
|
|
641
|
+
"num_threads",
|
|
642
|
+
"powerfmt",
|
|
643
|
+
"serde",
|
|
644
|
+
"time-core",
|
|
645
|
+
"time-macros",
|
|
646
|
+
]
|
|
647
|
+
|
|
648
|
+
[[package]]
|
|
649
|
+
name = "time-core"
|
|
650
|
+
version = "0.1.6"
|
|
651
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
652
|
+
checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
|
|
653
|
+
|
|
654
|
+
[[package]]
|
|
655
|
+
name = "time-macros"
|
|
656
|
+
version = "0.2.24"
|
|
657
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
658
|
+
checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
|
|
659
|
+
dependencies = [
|
|
660
|
+
"num-conv",
|
|
661
|
+
"time-core",
|
|
662
|
+
]
|
|
663
|
+
|
|
571
664
|
[[package]]
|
|
572
665
|
name = "unicode-ident"
|
|
573
666
|
version = "1.0.22"
|
data/ext/ratatui_ruby/Cargo.toml
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
[package]
|
|
5
5
|
name = "ratatui_ruby"
|
|
6
|
-
version = "0.
|
|
6
|
+
version = "0.2.0"
|
|
7
7
|
edition = "2021"
|
|
8
8
|
|
|
9
9
|
[lib]
|
|
@@ -11,6 +11,7 @@ crate-type = ["cdylib"]
|
|
|
11
11
|
|
|
12
12
|
[dependencies]
|
|
13
13
|
magnus = "0.6"
|
|
14
|
-
ratatui = "0.26"
|
|
14
|
+
ratatui = { version = "0.26", features = ["widget-calendar"] }
|
|
15
15
|
crossterm = "0.27"
|
|
16
16
|
lazy_static = "1.4"
|
|
17
|
+
time = { version = "0.3", features = ["macros"] }
|
data/ext/ratatui_ruby/extconf.rb
CHANGED
|
@@ -7,6 +7,6 @@ require "mkmf"
|
|
|
7
7
|
require "rb_sys/mkmf"
|
|
8
8
|
|
|
9
9
|
create_rust_makefile("ratatui_ruby/ratatui_ruby") do |r|
|
|
10
|
-
# Optional: Force release profile if needed
|
|
10
|
+
# Optional: Force release profile if needed
|
|
11
11
|
# r.profile = ENV.fetch("RB_SYS_CARGO_PROFILE", :release).to_sym
|
|
12
12
|
end
|
|
@@ -142,7 +142,10 @@ pub fn poll_event() -> Result<Value, Error> {
|
|
|
142
142
|
// Check if we are in test mode. If so, don't poll crossterm.
|
|
143
143
|
let is_test_mode = {
|
|
144
144
|
let term_lock = crate::terminal::TERMINAL.lock().unwrap();
|
|
145
|
-
matches!(
|
|
145
|
+
matches!(
|
|
146
|
+
term_lock.as_ref(),
|
|
147
|
+
Some(crate::terminal::TerminalWrapper::Test(_))
|
|
148
|
+
)
|
|
146
149
|
};
|
|
147
150
|
|
|
148
151
|
if is_test_mode {
|
|
@@ -23,8 +23,11 @@ pub fn render_node(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Err
|
|
|
23
23
|
"RatatuiRuby::Scrollbar" => widgets::scrollbar::render(frame, area, node)?,
|
|
24
24
|
"RatatuiRuby::BarChart" => widgets::barchart::render(frame, area, node)?,
|
|
25
25
|
"RatatuiRuby::Canvas" => widgets::canvas::render(frame, area, node)?,
|
|
26
|
+
"RatatuiRuby::Calendar" => widgets::calendar::render(frame, area, node)?,
|
|
26
27
|
"RatatuiRuby::Sparkline" => widgets::sparkline::render(frame, area, node)?,
|
|
27
|
-
"RatatuiRuby::
|
|
28
|
+
"RatatuiRuby::Chart" | "RatatuiRuby::LineChart" => {
|
|
29
|
+
widgets::chart::render(frame, area, node)?
|
|
30
|
+
}
|
|
28
31
|
_ => {}
|
|
29
32
|
}
|
|
30
33
|
Ok(())
|
|
@@ -40,12 +40,10 @@ pub fn init_terminal() -> Result<(), Error> {
|
|
|
40
40
|
|
|
41
41
|
pub fn init_test_terminal(width: u16, height: u16) -> Result<(), Error> {
|
|
42
42
|
let mut term_lock = TERMINAL.lock().unwrap();
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
*term_lock = Some(TerminalWrapper::Test(terminal));
|
|
48
|
-
}
|
|
43
|
+
let backend = TestBackend::new(width, height);
|
|
44
|
+
let terminal = Terminal::new(backend)
|
|
45
|
+
.map_err(|e| Error::new(magnus::exception::runtime_error(), e.to_string()))?;
|
|
46
|
+
*term_lock = Some(TerminalWrapper::Test(terminal));
|
|
49
47
|
Ok(())
|
|
50
48
|
}
|
|
51
49
|
|