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.
Files changed (119) hide show
  1. checksums.yaml +7 -0
  2. data/.build.yml +34 -0
  3. data/.pre-commit-config.yaml +9 -0
  4. data/.rubocop.yml +8 -0
  5. data/.ruby-version +1 -0
  6. data/AGENTS.md +119 -0
  7. data/CHANGELOG.md +15 -0
  8. data/CODE_OF_CONDUCT.md +30 -0
  9. data/CONTRIBUTING.md +40 -0
  10. data/LICENSE +15 -0
  11. data/LICENSES/AGPL-3.0-or-later.txt +661 -0
  12. data/LICENSES/BSD-2-Clause.txt +9 -0
  13. data/LICENSES/CC-BY-SA-4.0.txt +427 -0
  14. data/LICENSES/CC0-1.0.txt +121 -0
  15. data/LICENSES/MIT.txt +21 -0
  16. data/README.md +86 -0
  17. data/REUSE.toml +17 -0
  18. data/Rakefile +108 -0
  19. data/docs/application_testing.md +96 -0
  20. data/docs/contributors/design/ruby_frontend.md +100 -0
  21. data/docs/contributors/design/rust_backend.md +61 -0
  22. data/docs/contributors/design.md +11 -0
  23. data/docs/contributors/index.md +16 -0
  24. data/docs/images/examples-analytics.rb.png +0 -0
  25. data/docs/images/examples-box_demo.rb.png +0 -0
  26. data/docs/images/examples-dashboard.rb.png +0 -0
  27. data/docs/images/examples-login_form.rb.png +0 -0
  28. data/docs/images/examples-map_demo.rb.png +0 -0
  29. data/docs/images/examples-mouse_events.rb.png +0 -0
  30. data/docs/images/examples-scrollbar_demo.rb.png +0 -0
  31. data/docs/images/examples-stock_ticker.rb.png +0 -0
  32. data/docs/images/examples-system_monitor.rb.png +0 -0
  33. data/docs/index.md +18 -0
  34. data/docs/quickstart.md +126 -0
  35. data/examples/analytics.rb +87 -0
  36. data/examples/box_demo.rb +71 -0
  37. data/examples/dashboard.rb +72 -0
  38. data/examples/login_form.rb +114 -0
  39. data/examples/map_demo.rb +58 -0
  40. data/examples/mouse_events.rb +95 -0
  41. data/examples/scrollbar_demo.rb +75 -0
  42. data/examples/stock_ticker.rb +85 -0
  43. data/examples/system_monitor.rb +93 -0
  44. data/examples/test_analytics.rb +65 -0
  45. data/examples/test_box_demo.rb +38 -0
  46. data/examples/test_dashboard.rb +38 -0
  47. data/examples/test_login_form.rb +63 -0
  48. data/examples/test_map_demo.rb +100 -0
  49. data/examples/test_stock_ticker.rb +39 -0
  50. data/examples/test_system_monitor.rb +40 -0
  51. data/ext/ratatui_ruby/.cargo/config.toml +8 -0
  52. data/ext/ratatui_ruby/.gitignore +4 -0
  53. data/ext/ratatui_ruby/Cargo.lock +698 -0
  54. data/ext/ratatui_ruby/Cargo.toml +16 -0
  55. data/ext/ratatui_ruby/extconf.rb +12 -0
  56. data/ext/ratatui_ruby/src/events.rs +279 -0
  57. data/ext/ratatui_ruby/src/lib.rs +105 -0
  58. data/ext/ratatui_ruby/src/rendering.rs +31 -0
  59. data/ext/ratatui_ruby/src/style.rs +149 -0
  60. data/ext/ratatui_ruby/src/terminal.rs +131 -0
  61. data/ext/ratatui_ruby/src/widgets/barchart.rs +73 -0
  62. data/ext/ratatui_ruby/src/widgets/block.rs +12 -0
  63. data/ext/ratatui_ruby/src/widgets/canvas.rs +146 -0
  64. data/ext/ratatui_ruby/src/widgets/center.rs +81 -0
  65. data/ext/ratatui_ruby/src/widgets/cursor.rs +29 -0
  66. data/ext/ratatui_ruby/src/widgets/gauge.rs +50 -0
  67. data/ext/ratatui_ruby/src/widgets/layout.rs +82 -0
  68. data/ext/ratatui_ruby/src/widgets/linechart.rs +154 -0
  69. data/ext/ratatui_ruby/src/widgets/list.rs +62 -0
  70. data/ext/ratatui_ruby/src/widgets/mod.rs +18 -0
  71. data/ext/ratatui_ruby/src/widgets/overlay.rs +20 -0
  72. data/ext/ratatui_ruby/src/widgets/paragraph.rs +56 -0
  73. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +68 -0
  74. data/ext/ratatui_ruby/src/widgets/sparkline.rs +59 -0
  75. data/ext/ratatui_ruby/src/widgets/table.rs +117 -0
  76. data/ext/ratatui_ruby/src/widgets/tabs.rs +51 -0
  77. data/lib/ratatui_ruby/output.rb +7 -0
  78. data/lib/ratatui_ruby/schema/bar_chart.rb +28 -0
  79. data/lib/ratatui_ruby/schema/block.rb +23 -0
  80. data/lib/ratatui_ruby/schema/canvas.rb +62 -0
  81. data/lib/ratatui_ruby/schema/center.rb +19 -0
  82. data/lib/ratatui_ruby/schema/constraint.rb +33 -0
  83. data/lib/ratatui_ruby/schema/cursor.rb +17 -0
  84. data/lib/ratatui_ruby/schema/gauge.rb +24 -0
  85. data/lib/ratatui_ruby/schema/layout.rb +22 -0
  86. data/lib/ratatui_ruby/schema/line_chart.rb +41 -0
  87. data/lib/ratatui_ruby/schema/list.rb +22 -0
  88. data/lib/ratatui_ruby/schema/overlay.rb +15 -0
  89. data/lib/ratatui_ruby/schema/paragraph.rb +37 -0
  90. data/lib/ratatui_ruby/schema/scrollbar.rb +33 -0
  91. data/lib/ratatui_ruby/schema/sparkline.rb +24 -0
  92. data/lib/ratatui_ruby/schema/style.rb +31 -0
  93. data/lib/ratatui_ruby/schema/table.rb +24 -0
  94. data/lib/ratatui_ruby/schema/tabs.rb +22 -0
  95. data/lib/ratatui_ruby/test_helper.rb +75 -0
  96. data/lib/ratatui_ruby/version.rb +10 -0
  97. data/lib/ratatui_ruby.rb +87 -0
  98. data/sig/ratatui_ruby/ratatui_ruby.rbs +16 -0
  99. data/sig/ratatui_ruby/schema/bar_chart.rbs +14 -0
  100. data/sig/ratatui_ruby/schema/block.rbs +11 -0
  101. data/sig/ratatui_ruby/schema/canvas.rbs +62 -0
  102. data/sig/ratatui_ruby/schema/center.rbs +11 -0
  103. data/sig/ratatui_ruby/schema/constraint.rbs +13 -0
  104. data/sig/ratatui_ruby/schema/cursor.rbs +10 -0
  105. data/sig/ratatui_ruby/schema/gauge.rbs +13 -0
  106. data/sig/ratatui_ruby/schema/layout.rbs +11 -0
  107. data/sig/ratatui_ruby/schema/line_chart.rbs +20 -0
  108. data/sig/ratatui_ruby/schema/list.rbs +11 -0
  109. data/sig/ratatui_ruby/schema/overlay.rbs +9 -0
  110. data/sig/ratatui_ruby/schema/paragraph.rbs +11 -0
  111. data/sig/ratatui_ruby/schema/scrollbar.rbs +20 -0
  112. data/sig/ratatui_ruby/schema/sparkline.rbs +12 -0
  113. data/sig/ratatui_ruby/schema/style.rbs +13 -0
  114. data/sig/ratatui_ruby/schema/table.rbs +13 -0
  115. data/sig/ratatui_ruby/schema/tabs.rbs +11 -0
  116. data/sig/ratatui_ruby/test_helper.rbs +11 -0
  117. data/sig/ratatui_ruby/version.rbs +6 -0
  118. data/vendor/goodcop/base.yml +1047 -0
  119. 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