ratatui_ruby 0.2.0 → 0.3.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/.builds/ruby-3.2.yml +2 -2
- data/.builds/ruby-3.3.yml +2 -2
- data/.builds/ruby-3.4.yml +2 -2
- data/.builds/{ruby-4.0.0-preview3.yml → ruby-4.0.0.yml} +8 -9
- data/.pre-commit-config.yaml +9 -2
- data/AGENTS.md +59 -4
- data/CHANGELOG.md +58 -1
- data/README.md +6 -6
- data/REUSE.toml +1 -6
- data/{docs → doc}/contributors/index.md +2 -1
- data/doc/custom.css +8 -0
- data/doc/images/examples-custom_widget.rb.png +0 -0
- data/doc/images/examples-popup_demo.rb.gif +0 -0
- data/doc/images/examples-scroll_text.rb.png +0 -0
- data/doc/images/examples-table_select.rb.png +0 -0
- data/{docs → doc}/index.md +1 -1
- data/{docs → doc}/quickstart.md +24 -0
- data/examples/custom_widget.rb +43 -0
- data/examples/popup_demo.rb +105 -0
- data/examples/scroll_text.rb +74 -0
- data/examples/table_select.rb +70 -0
- data/examples/test_popup_demo.rb +62 -0
- data/examples/test_scroll_text.rb +130 -0
- data/examples/test_table_select.rb +37 -0
- data/ext/ratatui_ruby/Cargo.lock +167 -50
- data/ext/ratatui_ruby/Cargo.toml +4 -4
- data/ext/ratatui_ruby/src/buffer.rs +54 -0
- data/ext/ratatui_ruby/src/events.rs +111 -106
- data/ext/ratatui_ruby/src/lib.rs +15 -6
- data/ext/ratatui_ruby/src/rendering.rs +15 -0
- data/ext/ratatui_ruby/src/style.rs +2 -1
- data/ext/ratatui_ruby/src/terminal.rs +24 -19
- data/ext/ratatui_ruby/src/widgets/calendar.rs +4 -3
- data/ext/ratatui_ruby/src/widgets/canvas.rs +1 -2
- data/ext/ratatui_ruby/src/widgets/center.rs +0 -2
- data/ext/ratatui_ruby/src/widgets/chart.rs +11 -4
- data/ext/ratatui_ruby/src/widgets/clear.rs +37 -0
- data/ext/ratatui_ruby/src/widgets/cursor.rs +1 -1
- data/ext/ratatui_ruby/src/widgets/layout.rs +2 -1
- data/ext/ratatui_ruby/src/widgets/list.rs +6 -4
- data/ext/ratatui_ruby/src/widgets/mod.rs +1 -0
- data/ext/ratatui_ruby/src/widgets/overlay.rs +2 -1
- data/ext/ratatui_ruby/src/widgets/paragraph.rs +10 -0
- data/ext/ratatui_ruby/src/widgets/table.rs +25 -6
- data/ext/ratatui_ruby/src/widgets/tabs.rs +2 -1
- data/lib/ratatui_ruby/dsl.rb +2 -0
- data/lib/ratatui_ruby/schema/clear.rb +83 -0
- data/lib/ratatui_ruby/schema/paragraph.rb +7 -4
- data/lib/ratatui_ruby/schema/rect.rb +24 -0
- data/lib/ratatui_ruby/schema/table.rb +8 -2
- data/lib/ratatui_ruby/version.rb +1 -1
- data/lib/ratatui_ruby.rb +3 -1
- data/mise.toml +1 -1
- data/sig/ratatui_ruby/buffer.rbs +11 -0
- data/sig/ratatui_ruby/schema/rect.rbs +14 -0
- data/tasks/bump/changelog.rb +37 -0
- data/tasks/bump/comparison_links.rb +41 -0
- data/tasks/bump/header.rb +30 -0
- data/tasks/bump/history.rb +30 -0
- data/tasks/bump/manifest.rb +8 -0
- data/tasks/bump/ruby_gem.rb +6 -10
- data/tasks/bump/sem_ver.rb +6 -0
- data/tasks/bump/unreleased_section.rb +38 -0
- data/tasks/bump.rake +5 -1
- data/tasks/doc.rake +5 -4
- data/tasks/resources/build.yml.erb +1 -14
- data/tasks/resources/rubies.yml +1 -1
- data/tasks/sourcehut.rake +11 -2
- data/tasks/website/version.rb +1 -0
- data/tasks/website/version_menu.rb +68 -0
- data/tasks/website/versioned_documentation.rb +2 -1
- data/tasks/website/website.rb +4 -1
- data/tasks/website.rake +3 -3
- metadata +76 -26
- data/CODE_OF_CONDUCT.md +0 -30
- data/CONTRIBUTING.md +0 -40
- /data/{docs → doc}/application_testing.md +0 -0
- /data/{docs → doc}/contributors/design/ruby_frontend.md +0 -0
- /data/{docs → doc}/contributors/design/rust_backend.md +0 -0
- /data/{docs → doc}/contributors/design.md +0 -0
- /data/{docs → doc}/images/examples-analytics.rb.png +0 -0
- /data/{docs → doc}/images/examples-box_demo.rb.png +0 -0
- /data/{docs → doc}/images/examples-calendar_demo.rb.png +0 -0
- /data/{docs → doc}/images/examples-chart_demo.rb.png +0 -0
- /data/{docs → doc}/images/examples-dashboard.rb.png +0 -0
- /data/{docs → doc}/images/examples-list_styles.rb.png +0 -0
- /data/{docs → doc}/images/examples-login_form.rb.png +0 -0
- /data/{docs → doc}/images/examples-map_demo.rb.png +0 -0
- /data/{docs → doc}/images/examples-mouse_events.rb.png +0 -0
- /data/{docs → doc}/images/examples-quickstart_lifecycle.rb.png +0 -0
- /data/{docs → doc}/images/examples-scrollbar_demo.rb.png +0 -0
- /data/{docs → doc}/images/examples-stock_ticker.rb.png +0 -0
- /data/{docs → doc}/images/examples-system_monitor.rb.png +0 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
+
|
|
7
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
8
|
+
require "bundler/setup"
|
|
9
|
+
require "ratatui_ruby"
|
|
10
|
+
|
|
11
|
+
# Sample process data
|
|
12
|
+
PROCESSES = [
|
|
13
|
+
{ pid: 1234, name: "ruby", cpu: 15.2 },
|
|
14
|
+
{ pid: 5678, name: "postgres", cpu: 8.7 },
|
|
15
|
+
{ pid: 9012, name: "nginx", cpu: 3.1 },
|
|
16
|
+
{ pid: 3456, name: "redis", cpu: 12.4 },
|
|
17
|
+
{ pid: 7890, name: "sidekiq", cpu: 22.8 },
|
|
18
|
+
{ pid: 2345, name: "webpack", cpu: 45.3 },
|
|
19
|
+
{ pid: 6789, name: "node", cpu: 18.9 }
|
|
20
|
+
].freeze
|
|
21
|
+
|
|
22
|
+
selected_index = 0
|
|
23
|
+
|
|
24
|
+
RatatuiRuby.init_terminal
|
|
25
|
+
|
|
26
|
+
begin
|
|
27
|
+
loop do
|
|
28
|
+
# Create table rows from process data
|
|
29
|
+
rows = PROCESSES.map { |p| [p[:pid].to_s, p[:name], "#{p[:cpu]}%"] }
|
|
30
|
+
|
|
31
|
+
# Define column widths
|
|
32
|
+
widths = [
|
|
33
|
+
RatatuiRuby::Constraint.length(8),
|
|
34
|
+
RatatuiRuby::Constraint.length(15),
|
|
35
|
+
RatatuiRuby::Constraint.length(10)
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
# Create highlight style (yellow text)
|
|
39
|
+
highlight_style = RatatuiRuby::Style.new(fg: :yellow)
|
|
40
|
+
|
|
41
|
+
# Create table with selection
|
|
42
|
+
table = RatatuiRuby::Table.new(
|
|
43
|
+
header: ["PID", "Name", "CPU"],
|
|
44
|
+
rows: rows,
|
|
45
|
+
widths: widths,
|
|
46
|
+
selected_row: selected_index,
|
|
47
|
+
highlight_style: highlight_style,
|
|
48
|
+
highlight_symbol: "> ",
|
|
49
|
+
block: RatatuiRuby::Block.new(title: "Process Monitor (↑/↓ to select, q to quit)", borders: :all)
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Draw the table
|
|
53
|
+
RatatuiRuby.draw(table)
|
|
54
|
+
|
|
55
|
+
# Handle events
|
|
56
|
+
event = RatatuiRuby.poll_event
|
|
57
|
+
next unless event
|
|
58
|
+
|
|
59
|
+
case event[:code]
|
|
60
|
+
when "q"
|
|
61
|
+
break
|
|
62
|
+
when "down", "j"
|
|
63
|
+
selected_index = (selected_index + 1) % PROCESSES.length
|
|
64
|
+
when "up", "k"
|
|
65
|
+
selected_index = (selected_index - 1) % PROCESSES.length
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
ensure
|
|
69
|
+
RatatuiRuby.restore_terminal
|
|
70
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
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 "popup_demo"
|
|
11
|
+
|
|
12
|
+
class Minitest::Test
|
|
13
|
+
include RatatuiRuby::TestHelper
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class TestPopupDemo < Minitest::Test
|
|
17
|
+
def setup
|
|
18
|
+
@app = PopupDemo.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_render_initial_state
|
|
22
|
+
with_test_terminal(60, 20) do
|
|
23
|
+
@app.render
|
|
24
|
+
|
|
25
|
+
# Should have background text
|
|
26
|
+
assert buffer_content.any? { |line| line.include?("BACKGROUND RED") }
|
|
27
|
+
|
|
28
|
+
# Should have popup with "Clear is DISABLED" message
|
|
29
|
+
assert buffer_content.any? { |line| line.include?("Clear is DISABLED") }
|
|
30
|
+
assert buffer_content.any? { |line| line.include?("Style Bleed: Popup is RED!") }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_toggle_clear
|
|
35
|
+
with_test_terminal(60, 20) do
|
|
36
|
+
# Initial state: Clear disabled
|
|
37
|
+
@app.render
|
|
38
|
+
assert buffer_content.any? { |line| line.include?("Clear is DISABLED") }
|
|
39
|
+
|
|
40
|
+
# Toggle Clear on
|
|
41
|
+
inject_event("key", { code: " " })
|
|
42
|
+
@app.handle_input
|
|
43
|
+
|
|
44
|
+
@app.render
|
|
45
|
+
assert buffer_content.any? { |line| line.include?("Clear is ENABLED") }
|
|
46
|
+
assert buffer_content.any? { |line| line.include?("Resets background to default") }
|
|
47
|
+
|
|
48
|
+
# Toggle Clear off
|
|
49
|
+
inject_event("key", { code: " " })
|
|
50
|
+
@app.handle_input
|
|
51
|
+
|
|
52
|
+
@app.render
|
|
53
|
+
assert buffer_content.any? { |line| line.include?("Clear is DISABLED") }
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def test_quit
|
|
58
|
+
inject_event("key", { code: "q" })
|
|
59
|
+
status = @app.handle_input
|
|
60
|
+
assert_equal :quit, status
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
+
|
|
7
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
8
|
+
require "minitest/autorun"
|
|
9
|
+
require "ratatui_ruby/test_helper"
|
|
10
|
+
|
|
11
|
+
# Load the demo
|
|
12
|
+
require_relative "./scroll_text"
|
|
13
|
+
|
|
14
|
+
class TestScrollText < Minitest::Test
|
|
15
|
+
include RatatuiRuby::TestHelper
|
|
16
|
+
|
|
17
|
+
def setup
|
|
18
|
+
@demo = ScrollTextDemo.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_demo_initialization
|
|
22
|
+
assert_instance_of ScrollTextDemo, @demo
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_initial_rendering
|
|
26
|
+
with_test_terminal(60, 10) do
|
|
27
|
+
@demo.render
|
|
28
|
+
content = buffer_content
|
|
29
|
+
|
|
30
|
+
# Should show Line 1 at the top (inside the block border)
|
|
31
|
+
assert content[1].include?("Line 1")
|
|
32
|
+
# Should show title with scroll position 0, 0
|
|
33
|
+
assert content[0].include?("X: 0, Y: 0")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_scroll_down
|
|
38
|
+
inject_event("key", { code: "down" })
|
|
39
|
+
@demo.handle_input
|
|
40
|
+
|
|
41
|
+
with_test_terminal(60, 10) do
|
|
42
|
+
@demo.render
|
|
43
|
+
content = buffer_content
|
|
44
|
+
|
|
45
|
+
# After scrolling down once, Line 2 should be at the top
|
|
46
|
+
assert content[1].include?("Line 2"), "Expected Line 2 at top, got: #{content[1]}"
|
|
47
|
+
# Line 1 should not be visible (scrolled off)
|
|
48
|
+
refute content.any? { |line| line.include?("Line 1") }, "Line 1 should be scrolled off"
|
|
49
|
+
# Should show Y scroll position 1
|
|
50
|
+
assert content[0].include?("Y: 1")
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def test_scroll_right
|
|
55
|
+
inject_event("key", { code: "right" })
|
|
56
|
+
@demo.handle_input
|
|
57
|
+
|
|
58
|
+
with_test_terminal(60, 10) do
|
|
59
|
+
@demo.render
|
|
60
|
+
content = buffer_content
|
|
61
|
+
|
|
62
|
+
# After scrolling right once, first character should be cut off
|
|
63
|
+
# "Line 1:" becomes "ine 1:"
|
|
64
|
+
assert content[1].include?("ine 1:"), "Expected horizontal scroll, got: #{content[1]}"
|
|
65
|
+
# Should show X scroll position 1
|
|
66
|
+
assert content[0].include?("X: 1")
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def test_scroll_left_at_edge
|
|
71
|
+
# Try to scroll left when already at x=0
|
|
72
|
+
inject_event("key", { code: "left" })
|
|
73
|
+
@demo.handle_input
|
|
74
|
+
|
|
75
|
+
with_test_terminal(60, 10) do
|
|
76
|
+
@demo.render
|
|
77
|
+
content = buffer_content
|
|
78
|
+
|
|
79
|
+
# Should still show full "Line 1:" (can't scroll left past 0)
|
|
80
|
+
assert content[1].include?("Line 1:")
|
|
81
|
+
# Should still show X scroll position 0
|
|
82
|
+
assert content[0].include?("X: 0")
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def test_scroll_up_at_top
|
|
87
|
+
# Try to scroll up when already at top (should stay at 0)
|
|
88
|
+
inject_event("key", { code: "up" })
|
|
89
|
+
@demo.handle_input
|
|
90
|
+
|
|
91
|
+
with_test_terminal(60, 10) do
|
|
92
|
+
@demo.render
|
|
93
|
+
content = buffer_content
|
|
94
|
+
|
|
95
|
+
# Should still show Line 1 at top (can't scroll above 0)
|
|
96
|
+
assert content[1].include?("Line 1")
|
|
97
|
+
# Should still show Y scroll position 0
|
|
98
|
+
assert content[0].include?("Y: 0")
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def test_scroll_both_axes
|
|
103
|
+
# Scroll down 2 times and right 3 times
|
|
104
|
+
2.times do
|
|
105
|
+
inject_event("key", { code: "down" })
|
|
106
|
+
@demo.handle_input
|
|
107
|
+
end
|
|
108
|
+
3.times do
|
|
109
|
+
inject_event("key", { code: "right" })
|
|
110
|
+
@demo.handle_input
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
with_test_terminal(60, 10) do
|
|
114
|
+
@demo.render
|
|
115
|
+
content = buffer_content
|
|
116
|
+
|
|
117
|
+
# After scrolling down 2 and right 3, Line 3 should be at top with first 3 chars cut
|
|
118
|
+
# "Line 3:" becomes " 3:"
|
|
119
|
+
assert content[1].include?(" 3:"), "Expected Line 3 scrolled right by 3, got: #{content[1]}"
|
|
120
|
+
# Should show scroll position X: 3, Y: 2
|
|
121
|
+
assert content[0].include?("X: 3, Y: 2")
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def test_quit
|
|
126
|
+
inject_event("key", { code: "q" })
|
|
127
|
+
status = @demo.handle_input
|
|
128
|
+
assert_equal :quit, status
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
+
|
|
7
|
+
require "test_helper"
|
|
8
|
+
|
|
9
|
+
# Smoke test to ensure the table_select example can be loaded and instantiated
|
|
10
|
+
class TestTableSelect < Minitest::Test
|
|
11
|
+
def test_table_select_smoke
|
|
12
|
+
# Verify the example can create a table with selection
|
|
13
|
+
rows = [["1", "test", "10%"]]
|
|
14
|
+
widths = [
|
|
15
|
+
RatatuiRuby::Constraint.length(8),
|
|
16
|
+
RatatuiRuby::Constraint.length(15),
|
|
17
|
+
RatatuiRuby::Constraint.length(10)
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
table = RatatuiRuby::Table.new(
|
|
21
|
+
header: ["PID", "Name", "CPU"],
|
|
22
|
+
rows: rows,
|
|
23
|
+
widths: widths,
|
|
24
|
+
selected_row: 0,
|
|
25
|
+
highlight_style: RatatuiRuby::Style.new(fg: :yellow),
|
|
26
|
+
highlight_symbol: "> ",
|
|
27
|
+
block: RatatuiRuby::Block.new(title: "Test", borders: :all)
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# Verify it can be drawn
|
|
31
|
+
with_test_terminal(40, 10) do
|
|
32
|
+
RatatuiRuby.draw(table)
|
|
33
|
+
# Just verify it doesn't crash
|
|
34
|
+
assert true
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|