ratatui_ruby 0.1.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 +7 -0
- data/.build.yml +34 -0
- data/.pre-commit-config.yaml +9 -0
- data/.rubocop.yml +8 -0
- data/.ruby-version +1 -0
- data/AGENTS.md +119 -0
- data/CHANGELOG.md +15 -0
- data/CODE_OF_CONDUCT.md +30 -0
- data/CONTRIBUTING.md +40 -0
- data/LICENSE +15 -0
- data/LICENSES/AGPL-3.0-or-later.txt +661 -0
- data/LICENSES/BSD-2-Clause.txt +9 -0
- data/LICENSES/CC-BY-SA-4.0.txt +427 -0
- data/LICENSES/CC0-1.0.txt +121 -0
- data/LICENSES/MIT.txt +21 -0
- data/README.md +86 -0
- data/REUSE.toml +17 -0
- data/Rakefile +108 -0
- data/docs/application_testing.md +96 -0
- data/docs/contributors/design/ruby_frontend.md +100 -0
- data/docs/contributors/design/rust_backend.md +61 -0
- data/docs/contributors/design.md +11 -0
- data/docs/contributors/index.md +16 -0
- data/docs/images/examples-analytics.rb.png +0 -0
- data/docs/images/examples-box_demo.rb.png +0 -0
- data/docs/images/examples-dashboard.rb.png +0 -0
- data/docs/images/examples-login_form.rb.png +0 -0
- data/docs/images/examples-map_demo.rb.png +0 -0
- data/docs/images/examples-mouse_events.rb.png +0 -0
- data/docs/images/examples-scrollbar_demo.rb.png +0 -0
- data/docs/images/examples-stock_ticker.rb.png +0 -0
- data/docs/images/examples-system_monitor.rb.png +0 -0
- data/docs/index.md +18 -0
- data/docs/quickstart.md +126 -0
- data/examples/analytics.rb +87 -0
- data/examples/box_demo.rb +71 -0
- data/examples/dashboard.rb +72 -0
- data/examples/login_form.rb +114 -0
- data/examples/map_demo.rb +58 -0
- data/examples/mouse_events.rb +95 -0
- data/examples/scrollbar_demo.rb +75 -0
- data/examples/stock_ticker.rb +85 -0
- data/examples/system_monitor.rb +93 -0
- data/examples/test_analytics.rb +65 -0
- data/examples/test_box_demo.rb +38 -0
- data/examples/test_dashboard.rb +38 -0
- data/examples/test_login_form.rb +63 -0
- data/examples/test_map_demo.rb +100 -0
- data/examples/test_stock_ticker.rb +39 -0
- data/examples/test_system_monitor.rb +40 -0
- data/ext/ratatui_ruby/.cargo/config.toml +8 -0
- data/ext/ratatui_ruby/.gitignore +4 -0
- data/ext/ratatui_ruby/Cargo.lock +698 -0
- data/ext/ratatui_ruby/Cargo.toml +16 -0
- data/ext/ratatui_ruby/extconf.rb +12 -0
- data/ext/ratatui_ruby/src/events.rs +279 -0
- data/ext/ratatui_ruby/src/lib.rs +105 -0
- data/ext/ratatui_ruby/src/rendering.rs +31 -0
- data/ext/ratatui_ruby/src/style.rs +149 -0
- data/ext/ratatui_ruby/src/terminal.rs +131 -0
- data/ext/ratatui_ruby/src/widgets/barchart.rs +73 -0
- data/ext/ratatui_ruby/src/widgets/block.rs +12 -0
- data/ext/ratatui_ruby/src/widgets/canvas.rs +146 -0
- data/ext/ratatui_ruby/src/widgets/center.rs +81 -0
- data/ext/ratatui_ruby/src/widgets/cursor.rs +29 -0
- data/ext/ratatui_ruby/src/widgets/gauge.rs +50 -0
- data/ext/ratatui_ruby/src/widgets/layout.rs +82 -0
- data/ext/ratatui_ruby/src/widgets/linechart.rs +154 -0
- data/ext/ratatui_ruby/src/widgets/list.rs +62 -0
- data/ext/ratatui_ruby/src/widgets/mod.rs +18 -0
- data/ext/ratatui_ruby/src/widgets/overlay.rs +20 -0
- data/ext/ratatui_ruby/src/widgets/paragraph.rs +56 -0
- data/ext/ratatui_ruby/src/widgets/scrollbar.rs +68 -0
- data/ext/ratatui_ruby/src/widgets/sparkline.rs +59 -0
- data/ext/ratatui_ruby/src/widgets/table.rs +117 -0
- data/ext/ratatui_ruby/src/widgets/tabs.rs +51 -0
- data/lib/ratatui_ruby/output.rb +7 -0
- data/lib/ratatui_ruby/schema/bar_chart.rb +28 -0
- data/lib/ratatui_ruby/schema/block.rb +23 -0
- data/lib/ratatui_ruby/schema/canvas.rb +62 -0
- data/lib/ratatui_ruby/schema/center.rb +19 -0
- data/lib/ratatui_ruby/schema/constraint.rb +33 -0
- data/lib/ratatui_ruby/schema/cursor.rb +17 -0
- data/lib/ratatui_ruby/schema/gauge.rb +24 -0
- data/lib/ratatui_ruby/schema/layout.rb +22 -0
- data/lib/ratatui_ruby/schema/line_chart.rb +41 -0
- data/lib/ratatui_ruby/schema/list.rb +22 -0
- data/lib/ratatui_ruby/schema/overlay.rb +15 -0
- data/lib/ratatui_ruby/schema/paragraph.rb +37 -0
- data/lib/ratatui_ruby/schema/scrollbar.rb +33 -0
- data/lib/ratatui_ruby/schema/sparkline.rb +24 -0
- data/lib/ratatui_ruby/schema/style.rb +31 -0
- data/lib/ratatui_ruby/schema/table.rb +24 -0
- data/lib/ratatui_ruby/schema/tabs.rb +22 -0
- data/lib/ratatui_ruby/test_helper.rb +75 -0
- data/lib/ratatui_ruby/version.rb +10 -0
- data/lib/ratatui_ruby.rb +87 -0
- data/sig/ratatui_ruby/ratatui_ruby.rbs +16 -0
- data/sig/ratatui_ruby/schema/bar_chart.rbs +14 -0
- data/sig/ratatui_ruby/schema/block.rbs +11 -0
- data/sig/ratatui_ruby/schema/canvas.rbs +62 -0
- data/sig/ratatui_ruby/schema/center.rbs +11 -0
- data/sig/ratatui_ruby/schema/constraint.rbs +13 -0
- data/sig/ratatui_ruby/schema/cursor.rbs +10 -0
- data/sig/ratatui_ruby/schema/gauge.rbs +13 -0
- data/sig/ratatui_ruby/schema/layout.rbs +11 -0
- data/sig/ratatui_ruby/schema/line_chart.rbs +20 -0
- data/sig/ratatui_ruby/schema/list.rbs +11 -0
- data/sig/ratatui_ruby/schema/overlay.rbs +9 -0
- data/sig/ratatui_ruby/schema/paragraph.rbs +11 -0
- data/sig/ratatui_ruby/schema/scrollbar.rbs +20 -0
- data/sig/ratatui_ruby/schema/sparkline.rbs +12 -0
- data/sig/ratatui_ruby/schema/style.rbs +13 -0
- data/sig/ratatui_ruby/schema/table.rbs +13 -0
- data/sig/ratatui_ruby/schema/tabs.rbs +11 -0
- data/sig/ratatui_ruby/test_helper.rbs +11 -0
- data/sig/ratatui_ruby/version.rbs +6 -0
- data/vendor/goodcop/base.yml +1047 -0
- metadata +196 -0
|
@@ -0,0 +1,71 @@
|
|
|
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
|
+
class BoxDemoApp
|
|
10
|
+
def initialize
|
|
11
|
+
@color = "green"
|
|
12
|
+
@text = "Press Arrow Keys (q to quit)"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def run
|
|
16
|
+
RatatuiRuby.init_terminal
|
|
17
|
+
begin
|
|
18
|
+
loop do
|
|
19
|
+
render
|
|
20
|
+
break if handle_input == :quit
|
|
21
|
+
end
|
|
22
|
+
ensure
|
|
23
|
+
RatatuiRuby.restore_terminal
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def render
|
|
28
|
+
# 1. State/View
|
|
29
|
+
block = RatatuiRuby::Block.new(
|
|
30
|
+
title: "Box Demo",
|
|
31
|
+
borders: [:all],
|
|
32
|
+
border_color: @color
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
view_tree = RatatuiRuby::Paragraph.new(
|
|
36
|
+
text: @text,
|
|
37
|
+
fg: @color,
|
|
38
|
+
block:
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# 2. Render
|
|
42
|
+
RatatuiRuby.draw(view_tree)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def handle_input
|
|
46
|
+
# 3. Events
|
|
47
|
+
event = RatatuiRuby.poll_event
|
|
48
|
+
return unless event
|
|
49
|
+
|
|
50
|
+
if event[:type] == :key
|
|
51
|
+
case event[:code]
|
|
52
|
+
when "q"
|
|
53
|
+
:quit
|
|
54
|
+
when "up"
|
|
55
|
+
@color = "red"
|
|
56
|
+
@text = "Up Pressed!"
|
|
57
|
+
when "down"
|
|
58
|
+
@color = "blue"
|
|
59
|
+
@text = "Down Pressed!"
|
|
60
|
+
when "left"
|
|
61
|
+
@color = "yellow"
|
|
62
|
+
@text = "Left Pressed!"
|
|
63
|
+
when "right"
|
|
64
|
+
@color = "magenta"
|
|
65
|
+
@text = "Right Pressed!"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
BoxDemoApp.new.run if __FILE__ == $0
|
|
@@ -0,0 +1,72 @@
|
|
|
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
|
+
include RatatuiRuby
|
|
10
|
+
|
|
11
|
+
class DashboardApp
|
|
12
|
+
include RatatuiRuby
|
|
13
|
+
|
|
14
|
+
def initialize
|
|
15
|
+
@items = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5"]
|
|
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
|
+
end
|
|
26
|
+
ensure
|
|
27
|
+
RatatuiRuby.restore_terminal
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def render
|
|
32
|
+
selected_item = @items[@selected_index]
|
|
33
|
+
|
|
34
|
+
sidebar = List.new(
|
|
35
|
+
items: @items,
|
|
36
|
+
selected_index: @selected_index,
|
|
37
|
+
block: Block.new(title: "Files", borders: [:all])
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
main_content = Paragraph.new(
|
|
41
|
+
text: "You selected: #{selected_item}",
|
|
42
|
+
block: Block.new(title: "Content", borders: [:all])
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
layout = Layout.new(
|
|
46
|
+
direction: :horizontal,
|
|
47
|
+
constraints: [
|
|
48
|
+
Constraint.percentage(30),
|
|
49
|
+
Constraint.min(0),
|
|
50
|
+
],
|
|
51
|
+
children: [sidebar, main_content]
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
RatatuiRuby.draw(layout)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def handle_input
|
|
58
|
+
event = RatatuiRuby.poll_event
|
|
59
|
+
if event
|
|
60
|
+
case event[:code]
|
|
61
|
+
when "q", "esc"
|
|
62
|
+
:quit
|
|
63
|
+
when "up"
|
|
64
|
+
@selected_index = (@selected_index - 1) % @items.length
|
|
65
|
+
when "down"
|
|
66
|
+
@selected_index = (@selected_index + 1) % @items.length
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
DashboardApp.new.run if __FILE__ == $0
|
|
@@ -0,0 +1,114 @@
|
|
|
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_relative "../lib/ratatui_ruby"
|
|
7
|
+
|
|
8
|
+
class LoginFormApp
|
|
9
|
+
PREFIX = "Enter Username: [ "
|
|
10
|
+
SUFFIX = " ]"
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@username = ""
|
|
14
|
+
@show_popup = false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def run
|
|
18
|
+
RatatuiRuby.init_terminal
|
|
19
|
+
begin
|
|
20
|
+
loop do
|
|
21
|
+
render
|
|
22
|
+
break if handle_input == :quit
|
|
23
|
+
end
|
|
24
|
+
ensure
|
|
25
|
+
RatatuiRuby.restore_terminal
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def render
|
|
30
|
+
# 1. Base Layer Construction
|
|
31
|
+
# We want a cursor relative to the paragraph.
|
|
32
|
+
# So we wrap Paragraph and Cursor in an Overlay, and put that Overlay in a Center.
|
|
33
|
+
|
|
34
|
+
# Calculate cursor position
|
|
35
|
+
# Border takes 1 cell.
|
|
36
|
+
# Cursor X = 1 (border) + PREFIX.length + username.length
|
|
37
|
+
# Cursor Y = 1 (border + line 0)
|
|
38
|
+
cursor_x = 1 + PREFIX.length + @username.length
|
|
39
|
+
cursor_y = 1
|
|
40
|
+
|
|
41
|
+
# The content of the base form
|
|
42
|
+
form_content = RatatuiRuby::Overlay.new(layers: [
|
|
43
|
+
RatatuiRuby::Paragraph.new(
|
|
44
|
+
text: "#{PREFIX}#{@username}#{SUFFIX}",
|
|
45
|
+
block: RatatuiRuby::Block.new(borders: :all, title: "Login Form"),
|
|
46
|
+
align: :left
|
|
47
|
+
),
|
|
48
|
+
RatatuiRuby::Cursor.new(x: cursor_x, y: cursor_y),
|
|
49
|
+
])
|
|
50
|
+
|
|
51
|
+
# Center the form on screen
|
|
52
|
+
base_layer = RatatuiRuby::Center.new(
|
|
53
|
+
child: form_content,
|
|
54
|
+
width_percent: 50,
|
|
55
|
+
height_percent: 20
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# 2. Popup Layer Construction
|
|
59
|
+
final_view = if @show_popup
|
|
60
|
+
popup_message = RatatuiRuby::Center.new(
|
|
61
|
+
child: RatatuiRuby::Paragraph.new(
|
|
62
|
+
text: "Login Successful!\nPress 'q' to quit.",
|
|
63
|
+
style: RatatuiRuby::Style.new(fg: :green, bg: :black),
|
|
64
|
+
block: RatatuiRuby::Block.new(borders: :all),
|
|
65
|
+
align: :center,
|
|
66
|
+
wrap: true
|
|
67
|
+
),
|
|
68
|
+
width_percent: 30,
|
|
69
|
+
height_percent: 20
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Render Base Layer (background) THEN Popup Layer
|
|
73
|
+
RatatuiRuby::Overlay.new(layers: [base_layer, popup_message])
|
|
74
|
+
else
|
|
75
|
+
base_layer
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# 3. Draw
|
|
79
|
+
RatatuiRuby.draw(final_view)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def handle_input
|
|
83
|
+
# 4. Event Handling
|
|
84
|
+
event = RatatuiRuby.poll_event
|
|
85
|
+
return unless event
|
|
86
|
+
|
|
87
|
+
if event[:type] == :key
|
|
88
|
+
if @show_popup
|
|
89
|
+
# If popup is shown, any key (or specifically q) quits?
|
|
90
|
+
# "The Center widget contains a Green Box saying 'Login Successful! Press q.'"
|
|
91
|
+
if event[:code] == "q"
|
|
92
|
+
:quit
|
|
93
|
+
end
|
|
94
|
+
else
|
|
95
|
+
# Login Form Input
|
|
96
|
+
case event[:code]
|
|
97
|
+
when "enter"
|
|
98
|
+
@show_popup = true
|
|
99
|
+
when "backspace"
|
|
100
|
+
@username.chop!
|
|
101
|
+
when "esc"
|
|
102
|
+
:quit
|
|
103
|
+
else
|
|
104
|
+
# Simple text input
|
|
105
|
+
if event[:code].length == 1
|
|
106
|
+
@username += event[:code]
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
LoginFormApp.new.run if __FILE__ == $0
|
|
@@ -0,0 +1,58 @@
|
|
|
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
|
+
# An example of the Canvas widget showing a world map and animated shapes.
|
|
10
|
+
module MapDemo
|
|
11
|
+
include RatatuiRuby
|
|
12
|
+
|
|
13
|
+
# Returns a Canvas view for the map demo with the given circle radius.
|
|
14
|
+
#
|
|
15
|
+
# +radius+:: The radius of the animated circle.
|
|
16
|
+
def self.view(radius)
|
|
17
|
+
Canvas.new(
|
|
18
|
+
shapes: [
|
|
19
|
+
Map.new(color: :green, resolution: :high),
|
|
20
|
+
Circle.new(x: 0.0, y: 0.0, radius:, color: :red),
|
|
21
|
+
Line.new(x1: 0.0, y1: 0.0, x2: 50.0, y2: 25.0, color: :yellow),
|
|
22
|
+
],
|
|
23
|
+
x_bounds: [-180.0, 180.0],
|
|
24
|
+
y_bounds: [-90.0, 90.0],
|
|
25
|
+
marker: :braille,
|
|
26
|
+
block: Block.new(title: "World Map Canvas", borders: :all)
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Runs the map demo loop.
|
|
31
|
+
def self.run
|
|
32
|
+
RatatuiRuby.init_terminal
|
|
33
|
+
radius = 0.0
|
|
34
|
+
direction = 1
|
|
35
|
+
|
|
36
|
+
loop do
|
|
37
|
+
# Animate the circle radius
|
|
38
|
+
radius += 0.5 * direction
|
|
39
|
+
if radius > 10.0 || radius < 0.0
|
|
40
|
+
direction *= -1
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Define the view
|
|
44
|
+
view = view(radius)
|
|
45
|
+
|
|
46
|
+
RatatuiRuby.draw(view)
|
|
47
|
+
|
|
48
|
+
event = RatatuiRuby.poll_event
|
|
49
|
+
break if event && event[:type] == :key && event[:code] == "q"
|
|
50
|
+
|
|
51
|
+
sleep 0.05
|
|
52
|
+
end
|
|
53
|
+
ensure
|
|
54
|
+
RatatuiRuby.restore_terminal
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
MapDemo.run if __FILE__ == $PROGRAM_NAME
|
|
@@ -0,0 +1,95 @@
|
|
|
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
|
+
class MouseEventsApp
|
|
10
|
+
def initialize
|
|
11
|
+
@message = "Waiting for Mouse... (q to quit)"
|
|
12
|
+
@details = ""
|
|
13
|
+
@color = "white"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def run
|
|
17
|
+
RatatuiRuby.init_terminal
|
|
18
|
+
begin
|
|
19
|
+
loop do
|
|
20
|
+
render
|
|
21
|
+
break if handle_input == :quit
|
|
22
|
+
end
|
|
23
|
+
ensure
|
|
24
|
+
RatatuiRuby.restore_terminal
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def render
|
|
29
|
+
# Create a centered block with the mouse event details
|
|
30
|
+
block = RatatuiRuby::Block.new(
|
|
31
|
+
title: "Mouse Event Plumbing",
|
|
32
|
+
borders: [:all],
|
|
33
|
+
border_color: @color
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
content = RatatuiRuby::Paragraph.new(
|
|
37
|
+
text: "#{@message}\n#{@details}",
|
|
38
|
+
align: :center,
|
|
39
|
+
block:
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Use a layout to center the content
|
|
43
|
+
layout = RatatuiRuby::Layout.new(
|
|
44
|
+
direction: :vertical,
|
|
45
|
+
constraints: [
|
|
46
|
+
RatatuiRuby::Constraint.percentage(25),
|
|
47
|
+
RatatuiRuby::Constraint.percentage(50),
|
|
48
|
+
RatatuiRuby::Constraint.percentage(25),
|
|
49
|
+
],
|
|
50
|
+
children: [
|
|
51
|
+
RatatuiRuby::Paragraph.new(text: ""),
|
|
52
|
+
content,
|
|
53
|
+
RatatuiRuby::Paragraph.new(text: ""),
|
|
54
|
+
]
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
RatatuiRuby.draw(layout)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def handle_input
|
|
61
|
+
event = RatatuiRuby.poll_event
|
|
62
|
+
return unless event
|
|
63
|
+
|
|
64
|
+
if event[:type] == :key && event[:code] == "q"
|
|
65
|
+
return :quit
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
if event[:type] == :mouse
|
|
69
|
+
@color = "green"
|
|
70
|
+
case event[:kind]
|
|
71
|
+
when :down
|
|
72
|
+
@message = "#{event[:button].to_s.capitalize} Click at [#{event[:x]}, #{event[:y]}]"
|
|
73
|
+
@details = "Modifiers: #{event[:modifiers].join(', ')}"
|
|
74
|
+
when :up
|
|
75
|
+
@message = "#{event[:button].to_s.capitalize} Release at [#{event[:x]}, #{event[:y]}]"
|
|
76
|
+
@details = "Modifiers: #{event[:modifiers].join(', ')}"
|
|
77
|
+
when :drag
|
|
78
|
+
@message = "Dragging #{event[:button].to_s.capitalize} Button at [#{event[:x]}, #{event[:y]}]"
|
|
79
|
+
@details = "Modifiers: #{event[:modifiers].join(', ')}"
|
|
80
|
+
when :moved
|
|
81
|
+
@message = "Mouse Moved to [#{event[:x]}, #{event[:y]}]"
|
|
82
|
+
@details = "Modifiers: #{event[:modifiers].join(', ')}"
|
|
83
|
+
when :scroll_down
|
|
84
|
+
@message = "Scrolled Down"
|
|
85
|
+
@details = "Position: [#{event[:x]}, #{event[:y]}]"
|
|
86
|
+
when :scroll_up
|
|
87
|
+
@message = "Scrolled Up"
|
|
88
|
+
@details = "Position: [#{event[:x]}, #{event[:y]}]"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
nil
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
MouseEventsApp.new.run if __FILE__ == $0
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
# Simple Scrollbar Demo
|
|
10
|
+
class ScrollbarDemo
|
|
11
|
+
def initialize
|
|
12
|
+
@scroll_position = 0
|
|
13
|
+
@content_length = 50
|
|
14
|
+
@lines = (1..@content_length).map { |i| "Line #{i}" }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def run
|
|
18
|
+
RatatuiRuby.init_terminal
|
|
19
|
+
begin
|
|
20
|
+
loop do
|
|
21
|
+
draw
|
|
22
|
+
event = RatatuiRuby.poll_event
|
|
23
|
+
break if event && event[:type] == :key && event[:code] == "q"
|
|
24
|
+
|
|
25
|
+
handle_event(event)
|
|
26
|
+
end
|
|
27
|
+
ensure
|
|
28
|
+
RatatuiRuby.restore_terminal
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private def handle_event(event)
|
|
33
|
+
return unless event && event[:type] == :mouse
|
|
34
|
+
|
|
35
|
+
case event[:kind]
|
|
36
|
+
when :scroll_up
|
|
37
|
+
@scroll_position = [@scroll_position - 1, 0].max
|
|
38
|
+
when :scroll_down
|
|
39
|
+
@scroll_position = [@scroll_position + 1, @content_length].min
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private def draw
|
|
44
|
+
# Calculate visible lines based on scroll position
|
|
45
|
+
# In a real app, you'd want to know the height of the available area.
|
|
46
|
+
# For this demo, we'll just show all lines but offset the text.
|
|
47
|
+
visible_lines = @lines[@scroll_position..-1] || []
|
|
48
|
+
|
|
49
|
+
# Paragraph with content
|
|
50
|
+
p = RatatuiRuby::Paragraph.new(
|
|
51
|
+
text: visible_lines.join("\n"),
|
|
52
|
+
block: RatatuiRuby::Block.new(title: "Scroll with Mouse Wheel", borders: [:all])
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Scrollbar on the right
|
|
56
|
+
s = RatatuiRuby::Scrollbar.new(
|
|
57
|
+
content_length: @content_length,
|
|
58
|
+
position: @scroll_position
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Use a Layout to place scrollbar on the right
|
|
62
|
+
layout = RatatuiRuby::Layout.new(
|
|
63
|
+
direction: :horizontal,
|
|
64
|
+
constraints: [
|
|
65
|
+
RatatuiRuby::Constraint.percentage(95),
|
|
66
|
+
RatatuiRuby::Constraint.percentage(5),
|
|
67
|
+
],
|
|
68
|
+
children: [p, s]
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
RatatuiRuby.draw(layout)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
ScrollbarDemo.new.run if __FILE__ == $PROGRAM_NAME
|
|
@@ -0,0 +1,85 @@
|
|
|
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_relative "../lib/ratatui_ruby"
|
|
7
|
+
|
|
8
|
+
# Simulate a stock ticker
|
|
9
|
+
class StockTickerApp
|
|
10
|
+
def initialize
|
|
11
|
+
@network_data = Array.new(50) { rand(0..10) }
|
|
12
|
+
@ruby_stock = []
|
|
13
|
+
@rust_stock = []
|
|
14
|
+
@counter = 0
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def run
|
|
18
|
+
RatatuiRuby.init_terminal
|
|
19
|
+
begin
|
|
20
|
+
loop do
|
|
21
|
+
render
|
|
22
|
+
break if handle_input == :quit
|
|
23
|
+
sleep 0.1
|
|
24
|
+
end
|
|
25
|
+
ensure
|
|
26
|
+
RatatuiRuby.restore_terminal
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def render
|
|
31
|
+
# Update data
|
|
32
|
+
@counter += 1
|
|
33
|
+
@network_data.shift
|
|
34
|
+
@network_data << rand(0..10)
|
|
35
|
+
|
|
36
|
+
ruby_val = 50.0 + (Math.sin(@counter * 0.1) * 20.0) + (rand * 5.0)
|
|
37
|
+
rust_val = 40.0 + (Math.cos(@counter * 0.2) * 15.0) + (rand * 10.0)
|
|
38
|
+
|
|
39
|
+
@ruby_stock << [@counter.to_f, ruby_val]
|
|
40
|
+
@rust_stock << [@counter.to_f, rust_val]
|
|
41
|
+
|
|
42
|
+
# Keep only last 100 points
|
|
43
|
+
if @ruby_stock.length > 100
|
|
44
|
+
@ruby_stock.shift
|
|
45
|
+
@rust_stock.shift
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Define UI
|
|
49
|
+
ui = RatatuiRuby::Layout.new(
|
|
50
|
+
direction: :vertical,
|
|
51
|
+
constraints: [
|
|
52
|
+
RatatuiRuby::Constraint.new(type: :percentage, value: 20),
|
|
53
|
+
RatatuiRuby::Constraint.new(type: :percentage, value: 80),
|
|
54
|
+
],
|
|
55
|
+
children: [
|
|
56
|
+
RatatuiRuby::Sparkline.new(
|
|
57
|
+
data: @network_data,
|
|
58
|
+
style: RatatuiRuby::Style.new(fg: :cyan),
|
|
59
|
+
block: RatatuiRuby::Block.new(title: "Network Activity", borders: :all)
|
|
60
|
+
),
|
|
61
|
+
RatatuiRuby::LineChart.new(
|
|
62
|
+
datasets: [
|
|
63
|
+
RatatuiRuby::Dataset.new(name: "RBY", data: @ruby_stock, color: :green),
|
|
64
|
+
RatatuiRuby::Dataset.new(name: "RST", data: @rust_stock, color: :red),
|
|
65
|
+
],
|
|
66
|
+
x_labels: [@counter.to_s],
|
|
67
|
+
y_labels: %w[0 50 100],
|
|
68
|
+
y_bounds: [0.0, 100.0],
|
|
69
|
+
block: RatatuiRuby::Block.new(title: "Stock Ticker", borders: :all),
|
|
70
|
+
),
|
|
71
|
+
]
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
RatatuiRuby.draw(ui)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def handle_input
|
|
78
|
+
event = RatatuiRuby.poll_event
|
|
79
|
+
if event && event[:code] == "q"
|
|
80
|
+
:quit
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
StockTickerApp.new.run if __FILE__ == $0
|
|
@@ -0,0 +1,93 @@
|
|
|
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_relative "../lib/ratatui_ruby"
|
|
7
|
+
|
|
8
|
+
class SystemMonitorApp
|
|
9
|
+
def initialize
|
|
10
|
+
@percentage = 50
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def run
|
|
14
|
+
RatatuiRuby.init_terminal
|
|
15
|
+
begin
|
|
16
|
+
loop do
|
|
17
|
+
render
|
|
18
|
+
break if handle_input == :quit
|
|
19
|
+
end
|
|
20
|
+
ensure
|
|
21
|
+
RatatuiRuby.restore_terminal
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def render
|
|
26
|
+
# System Monitor Layout
|
|
27
|
+
# Top: Table (50%)
|
|
28
|
+
# Bottom: Gauge (50%)
|
|
29
|
+
|
|
30
|
+
header_style = RatatuiRuby::Style.new(fg: :blue, modifiers: [:bold])
|
|
31
|
+
|
|
32
|
+
header = [
|
|
33
|
+
RatatuiRuby::Paragraph.new(text: "PID", style: header_style),
|
|
34
|
+
RatatuiRuby::Paragraph.new(text: "Name", style: header_style),
|
|
35
|
+
RatatuiRuby::Paragraph.new(text: "CPU%", style: header_style),
|
|
36
|
+
RatatuiRuby::Paragraph.new(text: "Mem%", style: header_style),
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
rows = [
|
|
40
|
+
["1234", "ruby", "0.5", "1.2"],
|
|
41
|
+
["5678", "rust", "10.2", "4.5"],
|
|
42
|
+
["9012", "system", "1.1", "0.8"],
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
widths = [
|
|
46
|
+
RatatuiRuby::Constraint.length(10),
|
|
47
|
+
RatatuiRuby::Constraint.min(20),
|
|
48
|
+
RatatuiRuby::Constraint.length(10),
|
|
49
|
+
RatatuiRuby::Constraint.length(10),
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
table = RatatuiRuby::Table.new(
|
|
53
|
+
header:,
|
|
54
|
+
rows:,
|
|
55
|
+
widths:,
|
|
56
|
+
block: RatatuiRuby::Block.new(title: "Processes", borders: [:all])
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
gauge = RatatuiRuby::Gauge.new(
|
|
60
|
+
ratio: @percentage / 100.0,
|
|
61
|
+
label: "#{@percentage}%",
|
|
62
|
+
style: RatatuiRuby::Style.new(fg: :green),
|
|
63
|
+
block: RatatuiRuby::Block.new(title: "Memory Usage", borders: [:all])
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
layout = RatatuiRuby::Layout.new(
|
|
67
|
+
direction: :vertical,
|
|
68
|
+
children: [table, gauge],
|
|
69
|
+
constraints: [
|
|
70
|
+
RatatuiRuby::Constraint.percentage(50),
|
|
71
|
+
RatatuiRuby::Constraint.percentage(50),
|
|
72
|
+
]
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
RatatuiRuby.draw(layout)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def handle_input
|
|
79
|
+
event = RatatuiRuby.poll_event
|
|
80
|
+
if event
|
|
81
|
+
case event[:code]
|
|
82
|
+
when "q"
|
|
83
|
+
:quit
|
|
84
|
+
when "up"
|
|
85
|
+
@percentage = [@percentage + 5, 100].min
|
|
86
|
+
when "down"
|
|
87
|
+
@percentage = [@percentage - 5, 0].max
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
SystemMonitorApp.new.run if __FILE__ == $0
|